New upstream version 6.6.2
authorStuart Prescott <stuart@debian.org>
Sat, 16 Mar 2024 01:11:38 +0000 (12:11 +1100)
committerStuart Prescott <stuart@debian.org>
Sat, 16 Mar 2024 01:11:38 +0000 (12:11 +1100)
471 files changed:
.QT-ENTERPRISE-LICENSE-AGREEMENT
.flake8 [new file with mode: 0644]
.vscode/settings.json
README.md
README.pyside6_addons.md
build_history/blacklist.txt
build_scripts/main.py
build_scripts/options.py
build_scripts/platforms/linux.py
build_scripts/platforms/unix.py
build_scripts/platforms/windows_desktop.py
build_scripts/wheel_files.py
coin/dependencies.yaml
coin/instructions/common_environment.yaml
coin_build_instructions.py
coin_test_instructions.py
create_wheels.py
doc/changelogs/changes-6.6.1
doc/changelogs/changes-6.6.2 [new file with mode: 0644]
examples/async/eratosthenes/eratosthenes_asyncio.py
examples/async/minimal/minimal_asyncio.py
examples/axcontainer/axviewer/axviewer.py
examples/bluetooth/btscanner/device.py
examples/bluetooth/heartrate_game/connectionhandler.py
examples/bluetooth/heartrate_game/devicefinder.py
examples/bluetooth/heartrate_game/devicehandler.py
examples/bluetooth/heartrate_game/doc/heartrate_game.rst
examples/bluetooth/heartrate_game/heartrate_global.py
examples/bluetooth/heartrate_server/heartrate_server.py
examples/bluetooth/lowenergyscanner/characteristicinfo.py
examples/bluetooth/lowenergyscanner/device.py
examples/bluetooth/lowenergyscanner/deviceinfo.py
examples/bluetooth/lowenergyscanner/doc/lowenergyscanner.rst
examples/bluetooth/lowenergyscanner/main.py
examples/bluetooth/lowenergyscanner/serviceinfo.py
examples/charts/callout/callout.py
examples/charts/chartthemes/main.py
examples/charts/donutbreakdown/donutbreakdown.py
examples/charts/legend/legend.py
examples/charts/memoryusage/memoryusage.py
examples/charts/modeldata/modeldata.py
examples/charts/percentbarchart/percentbarchart.py
examples/charts/pointconfiguration/chartwindow.py
examples/charts/pointselectionandmarkers/pointselectionandmarkers.py
examples/charts/pointselectionandmarkers/utilities.py
examples/corelib/ipc/sharedmemory/dialog.py
examples/corelib/settingseditor/settingseditor.py
examples/corelib/threads/mandelbrot.py
examples/datavisualization/graphgallery/bargraph.py
examples/datavisualization/graphgallery/main.py
examples/datavisualization/graphgallery/variantbardataproxy.py
examples/datavisualization/qmlsurfacegallery/main.py
examples/dbus/pingpong/ping.py
examples/dbus/pingpong/pong.py
examples/demos/documentviewer/mainwindow.py
examples/demos/documentviewer/pdfviewer/pdfviewer.py
examples/demos/documentviewer/recentfiles.py
examples/demos/documentviewer/viewerfactory.py
examples/designer/taskmenuextension/registertictactoe.py
examples/designer/taskmenuextension/tictactoe.py
examples/designer/taskmenuextension/tictactoeplugin.py
examples/external/matplotlib/widget3d/widget3d.py
examples/external/opencv/webcam_pattern_detection.py
examples/external/scikit/staining_colors_separation.py
examples/graphs/widgetgallery/main.py
examples/graphs/widgetgallery/variantbardataproxy.py
examples/gui/analogclock/main.py
examples/gui/rhiwindow/main.py
examples/gui/rhiwindow/rhiwindow.py
examples/installer_test/hello.py
examples/location/mapviewer/doc/mapviewer.rst
examples/multimedia/audiooutput/audiooutput.py
examples/multimedia/audiooutput/doc/audiooutput.rst
examples/multimedia/audiosource/audiosource.py
examples/multimedia/audiosource/doc/audiosource.rst
examples/multimedia/camera/camera.py
examples/multimedia/camera/doc/camera.rst
examples/multimedia/camera/videosettings.py
examples/multimedia/player/player.py
examples/multimedia/screencapture/screenlistmodel.py
examples/network/blockingfortuneclient/blockingfortuneclient.py
examples/network/downloader/downloader.py
examples/network/fortuneclient/fortuneclient.py
examples/network/fortuneserver/fortuneserver.py
examples/network/googlesuggest/googlesuggest.py
examples/network/threadedfortuneserver/threadedfortuneserver.py
examples/networkauth/redditclient/redditmodel.py
examples/networkauth/redditclient/redditwrapper.py
examples/opengl/contextinfo/contextinfo.py
examples/opengl/hellogl2/mainwindow.py
examples/opengl/hellogl2/window.py
examples/opengl/textures/textures.py
examples/opengl/threadedqopenglwidget/renderer.py
examples/pdf/quickpdfviewer/main.py
examples/qml/editingmodel/doc/editingmodel.rst
examples/qml/editingmodel/main.py
examples/qml/editingmodel/model.py
examples/qml/tutorials/extending-qml-advanced/adding/main.py
examples/qml/tutorials/extending-qml-advanced/adding/person.py
examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.py
examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.py
examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.py
examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.py
examples/qml/tutorials/extending-qml-advanced/binding/main.py
examples/qml/tutorials/extending-qml-advanced/methods/main.py
examples/qml/tutorials/extending-qml-advanced/properties/main.py
examples/qml/tutorials/extending-qml-advanced/properties/person.py
examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/customPropertyTypes.py
examples/qml/tutorials/extending-qml/chapter5-listproperties/listproperties.py
examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/piechart.py
examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/pieslice.py
examples/qml/usingmodel/doc/usingmodel.rst
examples/qml/usingmodel/usingmodel.py
examples/qml/usingmodel/view.qml
examples/quick/models/objectlistmodel/doc/objectlistmodel.rst
examples/quick/models/objectlistmodel/objectlistmodel.py
examples/quick/models/stringlistmodel/doc/stringlistmodel.rst
examples/quick/models/stringlistmodel/stringlistmodel.py
examples/quick/painteditem/doc/painteditem.rst
examples/quick/rendercontrol/rendercontrol_opengl/cuberenderer.py
examples/quick/rendercontrol/rendercontrol_opengl/window_singlethreaded.py
examples/quick/scenegraph/openglunderqml/main.py
examples/quick/window/main.py
examples/quick3d/customgeometry/main.py
examples/quick3d/proceduraltexture/ProceduralTextureModule/Main.qml
examples/quick3d/proceduraltexture/gradienttexture.py
examples/quick3d/proceduraltexture/main.py
examples/quickcontrols/contactslist/Contact/ContactDelegate.ui.qml [new file with mode: 0644]
examples/quickcontrols/contactslist/Contact/ContactDialog.qml [new file with mode: 0644]
examples/quickcontrols/contactslist/Contact/ContactForm.ui.qml [new file with mode: 0644]
examples/quickcontrols/contactslist/Contact/ContactList.qml [new file with mode: 0644]
examples/quickcontrols/contactslist/Contact/ContactView.ui.qml [new file with mode: 0644]
examples/quickcontrols/contactslist/Contact/SectionDelegate.ui.qml [new file with mode: 0644]
examples/quickcontrols/contactslist/Contact/qmldir [new file with mode: 0644]
examples/quickcontrols/contactslist/contactlist.pyproject [new file with mode: 0644]
examples/quickcontrols/contactslist/contactmodel.py [new file with mode: 0644]
examples/quickcontrols/contactslist/doc/contactslist.rst [new file with mode: 0644]
examples/quickcontrols/contactslist/doc/qtquickcontrols-contactlist.png [new file with mode: 0644]
examples/quickcontrols/contactslist/main.py [new file with mode: 0644]
examples/quickcontrols/filesystemexplorer/filesystemexplorer.py
examples/quickcontrols/gallery/doc/gallery.rst
examples/quickcontrols/gallery/gallery.py
examples/serialbus/can/main.py
examples/serialbus/can/mainwindow.py
examples/serialbus/can/sendframebox.py
examples/serialbus/modbus/modbusclient/mainwindow.py
examples/serialport/terminal/mainwindow.py
examples/serialport/terminal/settingsdialog.py
examples/spatialaudio/audiopanning/main.py
examples/sql/books/bookdelegate.py
examples/sql/books/books.qrc
examples/sql/books/bookwindow.py
examples/sql/books/images/star-filled.svg [new file with mode: 0644]
examples/sql/books/images/star.png [deleted file]
examples/sql/books/images/star.svg [new file with mode: 0644]
examples/sql/books/main.py
examples/sql/books/rc_books.py
examples/statemachine/rogue/rogue.py
examples/utils/pyside_config.py
examples/webenginequick/nanobrowser/quicknanobrowser.py
examples/webenginewidgets/markdowneditor/main.py
examples/webenginewidgets/notifications/notificationpopup.py
examples/webenginewidgets/simplebrowser/browser.py
examples/webenginewidgets/simplebrowser/browserwindow.py
examples/webenginewidgets/simplebrowser/downloadwidget.py
examples/webenginewidgets/simplebrowser/main.py
examples/webenginewidgets/simplebrowser/webview.py
examples/webenginewidgets/widgetsnanobrowser/widgetsnanobrowser.py
examples/widgets/animation/animatedtiles/animatedtiles.py
examples/widgets/animation/appchooser/appchooser.py
examples/widgets/animation/easing/easing.py
examples/widgets/animation/states/states.py
examples/widgets/desktop/systray/window.py
examples/widgets/dialogs/classwizard/classwizard.py
examples/widgets/dialogs/classwizard/listchooser.py
examples/widgets/dialogs/standarddialogs/standarddialogs.py
examples/widgets/dialogs/trivialwizard/trivialwizard.py
examples/widgets/draganddrop/draggabletext/draggabletext.py
examples/widgets/effects/lighting/lighting.py
examples/widgets/gettext/doc/gettext.rst [new file with mode: 0644]
examples/widgets/graphicsview/anchorlayout/anchorlayout.py
examples/widgets/graphicsview/collidingmice/collidingmice.py
examples/widgets/graphicsview/diagramscene/diagramscene.py
examples/widgets/graphicsview/dragdroprobot/dragdroprobot.py
examples/widgets/graphicsview/elasticnodes/elasticnodes.py
examples/widgets/imageviewer/imageviewer.py
examples/widgets/itemviews/address_book/adddialogwidget.py
examples/widgets/itemviews/address_book/address_book.py
examples/widgets/itemviews/address_book/addresswidget.py
examples/widgets/itemviews/address_book/tablemodel.py
examples/widgets/itemviews/basicfiltermodel/basicsortfiltermodel.py
examples/widgets/itemviews/dirview/dirview.py
examples/widgets/itemviews/editabletreemodel/mainwindow.py
examples/widgets/itemviews/fetchmore/fetchmore.py
examples/widgets/itemviews/spinboxdelegate/spinboxdelegate.py
examples/widgets/itemviews/stardelegate/stardelegate.py
examples/widgets/itemviews/stardelegate/stareditor.py
examples/widgets/itemviews/stardelegate/starrating.py
examples/widgets/layouts/basiclayouts/basiclayouts.py
examples/widgets/layouts/dynamiclayouts/dynamiclayouts.py
examples/widgets/linguist/doc/linguist.rst [new file with mode: 0644]
examples/widgets/linguist/main.py
examples/widgets/mainwindows/application/application.py
examples/widgets/mainwindows/dockwidgets/dockwidgets.py
examples/widgets/mainwindows/mdi/mdi.py
examples/widgets/painting/basicdrawing/basicdrawing.py
examples/widgets/painting/concentriccircles/concentriccircles.py
examples/widgets/painting/painter/painter.py
examples/widgets/richtext/orderform/orderform.py
examples/widgets/richtext/syntaxhighlighter/syntaxhighlighter.py
examples/widgets/richtext/textedit/main.py
examples/widgets/richtext/textedit/textedit.py
examples/widgets/tools/regularexpression/regularexpressiondialog.py
examples/widgets/tutorials/addressbook/part2.py
examples/widgets/tutorials/addressbook/part3.py
examples/widgets/tutorials/addressbook/part4.py
examples/widgets/tutorials/addressbook/part5.py
examples/widgets/tutorials/addressbook/part6.py
examples/widgets/tutorials/addressbook/part7.py
examples/widgets/tutorials/cannon/t10.py
examples/widgets/tutorials/cannon/t11.py
examples/widgets/tutorials/cannon/t12.py
examples/widgets/tutorials/cannon/t13.py
examples/widgets/tutorials/cannon/t14.py
examples/widgets/tutorials/cannon/t4.py
examples/widgets/tutorials/cannon/t5.py
examples/widgets/tutorials/cannon/t6.py
examples/widgets/tutorials/cannon/t7.py
examples/widgets/tutorials/cannon/t8.py
examples/widgets/tutorials/cannon/t9.py
examples/widgets/tutorials/modelview/2_formatting.py
examples/widgets/tutorials/modelview/3_changingmodel.py
examples/widgets/tutorials/modelview/6_treeview.py
examples/widgets/tutorials/modelview/7_selections.py
examples/widgets/widgets/charactermap/characterwidget.py
examples/widgets/widgets/charactermap/mainwindow.py
examples/widgets/widgets/digitalclock/doc/digitalclock.rst
examples/widgets/widgets/tetrix/tetrix.py
examples/widgets/widgetsgallery/widgetgallery.py
examples/xml/dombookmarks/dombookmarks.py
requirements-doc.txt
requirements.txt
sources/pyside-tools/CMakeLists.txt
sources/pyside-tools/android_deploy.py
sources/pyside-tools/deploy.py
sources/pyside-tools/deploy.pyproject
sources/pyside-tools/deploy_lib/__init__.py
sources/pyside-tools/deploy_lib/android/__init__.py
sources/pyside-tools/deploy_lib/android/android_config.py [new file with mode: 0644]
sources/pyside-tools/deploy_lib/android/android_helper.py
sources/pyside-tools/deploy_lib/android/buildozer.py
sources/pyside-tools/deploy_lib/android/recipes/PySide6/__init__.tmpl.py
sources/pyside-tools/deploy_lib/config.py
sources/pyside-tools/deploy_lib/default.spec
sources/pyside-tools/deploy_lib/deploy_util.py
sources/pyside-tools/deploy_lib/nuitka_helper.py
sources/pyside-tools/deploy_lib/pyside_icon.icns [new file with mode: 0644]
sources/pyside-tools/deploy_lib/pyside_icon.ico [new file with mode: 0644]
sources/pyside-tools/deploy_lib/python_helper.py
sources/pyside-tools/project.py
sources/pyside-tools/project/newproject.py
sources/pyside-tools/project/project_data.py
sources/pyside-tools/pyside_tool.py
sources/pyside-tools/qml.py
sources/pyside-tools/qtpy2cpp_lib/tests/baseline/basic_test.py
sources/pyside-tools/qtpy2cpp_lib/tests/test_qtpy2cpp.py
sources/pyside-tools/qtpy2cpp_lib/visitor.py
sources/pyside-tools/requirements-android.txt [new file with mode: 0644]
sources/pyside6/.cmake.conf
sources/pyside6/CMakeLists.txt
sources/pyside6/PySide6/QtAsyncio/__init__.py
sources/pyside6/PySide6/QtAsyncio/events.py
sources/pyside6/PySide6/QtAsyncio/futures.py
sources/pyside6/PySide6/QtAsyncio/tasks.py
sources/pyside6/PySide6/QtCharts/CMakeLists.txt
sources/pyside6/PySide6/QtCharts/typesystem_charts.xml
sources/pyside6/PySide6/QtCore/typesystem_core_common.xml
sources/pyside6/PySide6/QtDBus/typesystem_dbus.xml
sources/pyside6/PySide6/QtGui/CMakeLists.txt
sources/pyside6/PySide6/QtSql/typesystem_sql.xml
sources/pyside6/PySide6/glue/qtcore.cpp
sources/pyside6/PySide6/glue/qtnetworkauth.cpp
sources/pyside6/PySide6/glue/qtuitools.cpp
sources/pyside6/PySide6/glue/qtwebenginecore.cpp
sources/pyside6/PySide6/glue/qtwebenginewidgets.cpp
sources/pyside6/PySide6/templates/gui_common.xml
sources/pyside6/cmake/Macros/PySideModules.cmake
sources/pyside6/cmake/PySideSetup.cmake
sources/pyside6/doc/CMakeLists.txt
sources/pyside6/doc/PySide6/QtAsyncio/index.rst [new file with mode: 0644]
sources/pyside6/doc/_static/qtforpython.ico [new file with mode: 0644]
sources/pyside6/doc/additionaldocs.lst
sources/pyside6/doc/commercial/index.rst
sources/pyside6/doc/conf.py.in
sources/pyside6/doc/contents.rst
sources/pyside6/doc/developer/adapt_qt.rst [new file with mode: 0644]
sources/pyside6/doc/developer/add_module.rst
sources/pyside6/doc/developer/add_port_example.rst
sources/pyside6/doc/developer/documentation.rst
sources/pyside6/doc/developer/feature-motivation.rst
sources/pyside6/doc/developer/index.rst
sources/pyside6/doc/developer/signature_doc.rst
sources/pyside6/doc/extras/QtBluetooth.rst
sources/pyside6/doc/extras/QtCore.ClassInfo.rst
sources/pyside6/doc/extras/QtCore.Property.rst
sources/pyside6/doc/extras/QtCore.Signal.rst
sources/pyside6/doc/extras/QtCore.Slot.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerContainerExtension.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerCustomWidgetCollection.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerMemberSheetExtension.rst
sources/pyside6/doc/extras/QtDesigner.QPyDesignerTaskMenuExtension.rst
sources/pyside6/doc/extras/QtMultimedia.rst
sources/pyside6/doc/extras/QtQml.QPyQmlParserStatus.rst
sources/pyside6/doc/extras/QtQml.QPyQmlPropertyValueSource.rst
sources/pyside6/doc/extras/QtQml.QmlSingleton.rst
sources/pyside6/doc/extras/QtQml.QmlUncreatable.rst
sources/pyside6/doc/extras/QtUiTools.loadUiType.rst
sources/pyside6/doc/faq/typesoffiles.rst
sources/pyside6/doc/faq/whatisshiboken.rst
sources/pyside6/doc/gettingstarted/linux.rst
sources/pyside6/doc/gettingstarted/porting_from2.rst
sources/pyside6/doc/inheritance_graph.py
sources/pyside6/doc/modules.rst
sources/pyside6/doc/qdoc_spawner.py.in [new file with mode: 0644]
sources/pyside6/doc/quickstart.rst
sources/pyside6/doc/tutorials/basictutorial/clickablebutton.rst
sources/pyside6/doc/tutorials/basictutorial/dialog.rst
sources/pyside6/doc/tutorials/basictutorial/translations.rst
sources/pyside6/doc/tutorials/basictutorial/treewidget.rst
sources/pyside6/doc/tutorials/basictutorial/uifiles.rst
sources/pyside6/doc/tutorials/expenses/expenses.rst
sources/pyside6/doc/tutorials/index.rst
sources/pyside6/doc/tutorials/portingguide/chapter1/chapter1.rst
sources/pyside6/doc/tutorials/portingguide/chapter2/chapter2.rst
sources/pyside6/doc/tutorials/portingguide/chapter3/chapter3.rst
sources/pyside6/doc/tutorials/qmlsqlintegration/qmlsqlintegration.rst
sources/pyside6/doc/videos.rst
sources/pyside6/libpyside/CMakeLists.txt
sources/pyside6/libpyside/globalreceiverv2.cpp
sources/pyside6/libpyside/globalreceiverv2.h
sources/pyside6/libpyside/pysideqslotobject_p.cpp [new file with mode: 0644]
sources/pyside6/libpyside/pysideqslotobject_p.h [new file with mode: 0644]
sources/pyside6/libpyside/pysidesignal.cpp
sources/pyside6/libpyside/pysidesignal_p.h
sources/pyside6/libpyside/qobjectconnect.cpp
sources/pyside6/libpyside/qobjectconnect.h
sources/pyside6/libpysideqml/pysideqmlregistertype.h
sources/pyside6/plugins/designer/designercustomwidgets.cpp
sources/pyside6/tests/CMakeLists.txt
sources/pyside6/tests/QtAsyncio/qasyncio_test.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_cancel_task.py [new file with mode: 0644]
sources/pyside6/tests/QtAsyncio/qasyncio_test_chain.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_executor.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_queues.py
sources/pyside6/tests/QtAsyncio/qasyncio_test_threadsafe.py [new file with mode: 0644]
sources/pyside6/tests/QtAsyncio/qasyncio_test_time.py
sources/pyside6/tests/QtBluetooth/localdevice.py
sources/pyside6/tests/QtCore/bug_515.py
sources/pyside6/tests/QtCore/classinfo_test.py
sources/pyside6/tests/QtCore/feature_with_uic/window.py
sources/pyside6/tests/QtCore/loggingcategorymacros_test.py
sources/pyside6/tests/QtCore/multiple_feature_test.py
sources/pyside6/tests/QtCore/qcbor_test.py
sources/pyside6/tests/QtCore/qobject_parent_test.py
sources/pyside6/tests/QtCore/qresource_test.py
sources/pyside6/tests/QtCore/qtimer_singleshot_test.py
sources/pyside6/tests/QtCore/snake_prop_feature_test.py
sources/pyside6/tests/QtDataVisualization/datavisualization_test.py
sources/pyside6/tests/QtGui/qcolor_test.py
sources/pyside6/tests/QtGui/qfontmetrics_test.py
sources/pyside6/tests/QtGui/qicon_test.py
sources/pyside6/tests/QtOpenGL/qopenglwindow_test.py
sources/pyside6/tests/QtQml/bug_451.py
sources/pyside6/tests/QtQml/bug_456.py
sources/pyside6/tests/QtQml/bug_557.py
sources/pyside6/tests/QtQml/bug_726.py
sources/pyside6/tests/QtQml/bug_814.py
sources/pyside6/tests/QtQml/bug_825.py
sources/pyside6/tests/QtQml/bug_926.py
sources/pyside6/tests/QtQml/bug_995.py
sources/pyside6/tests/QtQml/connect_python_qml.py
sources/pyside6/tests/QtQml/qquickview_test.py
sources/pyside6/tests/QtQml/registersingletontype.py
sources/pyside6/tests/QtQml/signal_arguments.py
sources/pyside6/tests/QtUiTools/bug_1060.py
sources/pyside6/tests/QtUiTools/bug_552.py
sources/pyside6/tests/QtUiTools/bug_797.py
sources/pyside6/tests/QtUiTools/bug_958.py
sources/pyside6/tests/QtWidgets/bug_400.py
sources/pyside6/tests/QtWidgets/bug_667.py
sources/pyside6/tests/QtWidgets/bug_854.py
sources/pyside6/tests/QtWidgets/paint_event_test.py
sources/pyside6/tests/QtWidgets/qlabel_test.py
sources/pyside6/tests/QtWidgets/qmenu_test.py
sources/pyside6/tests/QtWidgets/qstyle_test.py
sources/pyside6/tests/QtWidgets/qtreeview_test.py
sources/pyside6/tests/QtWidgets/qvariant_test.py
sources/pyside6/tests/pysidetest/constructor_properties_test.py
sources/pyside6/tests/pysidetest/container_test.py
sources/pyside6/tests/pysidetest/enum_test.py
sources/pyside6/tests/pysidetest/homonymoussignalandmethod_test.py
sources/pyside6/tests/pysidetest/multiple_inheritance_test.py
sources/pyside6/tests/pysidetest/pyenum_relax_options_test.py
sources/pyside6/tests/pysidetest/signalinstance_equality_test.py
sources/pyside6/tests/pysidetest/snake_case_sub.py
sources/pyside6/tests/pysidetest/snake_case_test.py
sources/pyside6/tests/pysidetest/true_property_test.py
sources/pyside6/tests/registry/scrape_testresults.py
sources/pyside6/tests/registry/util.py
sources/pyside6/tests/signals/invalid_callback_test.py
sources/pyside6/tests/signals/qobject_sender_test.py
sources/pyside6/tests/tools/list-class-hierarchy.py
sources/pyside6/tests/tools/pyside6-android-deploy/test_pyside6_android_deploy.py
sources/pyside6/tests/tools/pyside6-deploy/extensive_deploy_test.py
sources/pyside6/tests/tools/pyside6-deploy/test_pyside6_deploy.py
sources/pyside6/tests/util/helper/docmodifier.py
sources/pyside6/tests/util/helper/usesqapplication.py
sources/pyside6/tests/util/processtimer.py
sources/shiboken6/.cmake.conf
sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp
sources/shiboken6/ApiExtractor/abstractmetafunction.cpp
sources/shiboken6/ApiExtractor/abstractmetalang.cpp
sources/shiboken6/ApiExtractor/abstractmetatype.cpp
sources/shiboken6/ApiExtractor/abstractmetatype.h
sources/shiboken6/ApiExtractor/clangparser/clangutils.cpp
sources/shiboken6/ApiExtractor/clangparser/clangutils.h
sources/shiboken6/ApiExtractor/doxygenparser.cpp
sources/shiboken6/ApiExtractor/include.cpp
sources/shiboken6/ApiExtractor/include.h
sources/shiboken6/ApiExtractor/messages.cpp
sources/shiboken6/ApiExtractor/messages.h
sources/shiboken6/cmake/ShibokenHelpers.cmake
sources/shiboken6/cmake/ShibokenSetup.cmake
sources/shiboken6/data/Shiboken6Config-spec.cmake.in
sources/shiboken6/data/shiboken6.pc.in
sources/shiboken6/doc/CMakeLists.txt
sources/shiboken6/doc/conf.py.in
sources/shiboken6/doc/shibokengenerator.rst
sources/shiboken6/doc/typesystem_specifying_types.rst
sources/shiboken6/generator/CMakeLists.txt
sources/shiboken6/generator/generator.cpp
sources/shiboken6/generator/qtdoc/qtdocgenerator.cpp
sources/shiboken6/generator/qtdoc/qtxmltosphinx.cpp
sources/shiboken6/generator/shiboken/cppgenerator.cpp
sources/shiboken6/generator/shiboken/cppgenerator.h
sources/shiboken6/generator/shiboken/headergenerator.cpp
sources/shiboken6/generator/shiboken/headergenerator.h
sources/shiboken6/generator/shiboken/shibokengenerator.cpp
sources/shiboken6/generator/shiboken/shibokengenerator.h
sources/shiboken6/libshiboken/CMakeLists.txt
sources/shiboken6/libshiboken/basewrapper.cpp
sources/shiboken6/libshiboken/helper.cpp
sources/shiboken6/libshiboken/pep384impl.cpp
sources/shiboken6/libshiboken/pep384impl.h
sources/shiboken6/libshiboken/sbkversion.h.in
sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/mapping.py
sources/shiboken6/tests/CMakeLists.txt
sources/shiboken6/tests/shibokenmodule/module_test.py
testing/buildlog.py
testing/command.py
testing/runner.py
testing/wheel_tester.py
tools/cross_compile_android/android_utilities.py
tools/cross_compile_android/main.py
tools/cross_compile_android/templates/cross_compile.tmpl.sh
tools/cross_compile_android/templates/toolchain_default.tmpl.cmake
tools/doc_modules.py
wheel_artifacts/pyproject.toml.base
wheel_artifacts/setup.py.base

index ff775f951e1915a6fd3462fd01fbc42c3dc56ae0..2d225ecf6e9385d9c7e83ba20ab3d771ef44f94f 100644 (file)
-QT LICENSE AGREEMENT
-Agreement version 4.4.1
-
-This Qt License Agreement ("Agreement") is a legal agreement for the licensing
-of Licensed Software (as defined below) between The Qt Company (as defined
-below) and the Licensee who has accepted the terms of this Agreement by signing
-this Agreement or by downloading or using the Licensed Software or in any other
-appropriate means.
-
-Capitalized terms used herein are defined in Section 1.
-
-WHEREAS:
-    (A) Licensee wishes to use the Licensed Software for the purpose of
-    developing and distributing Applications and/or Devices (each as defined
-    below);
-    (B) The Qt Company is willing to grant the Licensee a right to use Licensed
-    Software for such a purpose pursuant to term and conditions of this
-    Agreement; and
-    (C) Parties wish to enable that their respective Affiliates also can sell
-    and purchase licenses to serve Licensee Affiliates' needs to use Licensed
-    Software pursuant to terms of the Agreement. Any such license purchases by
-    Licensee Affiliates from The Qt Company or its Affiliates will create
-    contractual relationship directly between the relevant The Qt Company and
-    the respective ordering Licensee Affiliate "Acceding Agreement").
-    Accordingly, Licensee shall not be a party to any such Acceding Agreement,
-    and no rights or obligations are created to the Licensee thereunder but all
-    rights and obligations under such Acceding Agreement are vested and borne
-    solely by the ordering Licensee Affiliate and the relevant The Qt Company
-    as a contracting parties under such Acceding Agreement.
-
-NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS:
-
-1. DEFINITIONS
-
-"Affiliate" of a Party shall mean an entity
-    (i) which is directly or indirectly controlling such Party;
-    (ii) which is under the same direct or indirect ownership or control as
-        such Party; or
-    (iii) which is directly or indirectly owned or controlled by such Party.
-For these purposes, an entity shall be treated as being controlled by another
-if that other entity has fifty percent (50 %) or more of the votes in such
-entity, is able to direct its affairs and/or to control the composition of its
-board of directors or equivalent body.
-
-"Add-on Products" shall mean The Qt Company's specific add-on software products
-which are not licensed as part of The Qt Company's standard product offering,
-but shall be included into the scope of Licensed Software only if so
-specifically agreed between the Parties.
-
-"Agreement Term" shall mean the validity period of this Agreement, as set forth
-in Section 12.
-
-"Applications" shall mean software products created using the Licensed
-Software, which include the Redistributables, or part thereof.
-
-"Contractor(s)" shall mean third party consultants, distributors and
-contractors performing services to the Licensee under applicable contractual
-arrangement.
-
-"Customer(s)" shall mean Licensee's customers to whom Licensee, directly or
-indirectly, distributes copies of the Redistributables as integrated or
-incorporated into Applications or Devices.
-
-"Data Protection Legislation" shall mean the General Data Protection Regulation
-(EU 2016/679) (GDPR) and any national implementing laws, regulations and
-secondary legislation, as may be amended or updated from time to time, as well
-as any other data protection laws or regulations applicable in relevant
-territory.
-
-"Deployment Platforms" shall mean target operating systems and/or hardware
-specified in the License Certificate, on which the Redistributables can be
-distributed pursuant to the terms and conditions of this Agreement.
-
-"Designated User(s)" shall mean the employee(s) of Licensee or Licensee's
-Affiliates acting within the scope of their employment or Licensee's
-Contractors acting within the scope of their services on behalf of Licensee.
-
-"Development License" shall mean the license needed by the Licensee for each
-Designated User to use the Licensed Software under the license grant described
-in Section 3.1 of this Agreement. Development Licenses are available per
-respective Licensed Software products, each product having its designated scope
-and purpose of use.
-
-"Development License Term" shall mean the agreed validity period of the
-Development License or QA Tools license during which time the relevant Licensed
-Software product can be used pursuant to this Agreement. Agreed Development
-License Term, as ordered and paid for by the Licensee, shall be memorialized in
-the applicable License Certificate.
-
-"Development Platforms" shall mean those host operating systems specified in
-the License Certificate, in which the Licensed Software can be used under the
-Development License.
-
-"Devices" shall mean
-    (1) hardware devices or products that
-        i. are manufactured and/or distributed by the Licensee, its Affiliates,
-           Contractors or Customers, and
-        ii. incorporate, integrate or link to Applications such that
-           substantial functionality of such unit, when used by an End User,
-           is provided by Application(s) or otherwise depends on the Licensed
-           Software, regardless of whether the Application is developed by
-           Licensee or its Contractors; or
-    (2) Applications designed for the hardware devices specified in item (1).
-
-    Devices covered by this Agreement shall be specified in Appendix 2 or in a
-    quote.
-
-"Distribution License(s)" shall mean a royalty-bearing license required for any
-kind of sale, trade, exchange, loan, lease, rental or other distribution by or
-on behalf of Licensee to a third party of Redistributables in connection with
-Devices pursuant to license grant described in Section 3.3 of this Agreement.
-Distribution Licensed are sold separately for each type of Device respectively
-and cannot be used for any type of Devices at Licensee's discretion.
-
-"Distribution License Packs" shall mean set of prepaid Distribution Licenses
-for distribution of Redistributables, as defined in The Qt Company's standard
-price list, quote, Purchase Order confirmation or in an Appendix 2 hereto, as
-the case may be.
-
-"End User" shall mean the final end user of the Application or a Device.
-
-"Evaluation License Term" shall mean a time period specified in the License
-Certificate for the Licensee to use the relevant Licensed Software for
-evaluation purposes according to Section 3.6 herein.
-
-"Intellectual Property Rights" shall mean patents (including utility models),
-design patents, and designs (whether or not capable of registration), chip
-topography rights and other like protection, copyrights, trademarks, service
-marks, trade names, logos or other words or symbols and any other form of
-statutory protection of any kind and applications for any of the foregoing as
-well as any trade secrets.
-
-"License Certificate" shall mean a certificate generated by The Qt Company for
-each Designated User respectively upon them downloading the Licensed Software,
-which will be available under respective Designated User's Qt Account at
-account.qt.io. License Certificates will specify relevant information
-pertaining the Licensed Software purchased by Licensee and Designated User's
-license to the Licensed Software.
-
-"License Fee" shall mean the fee charged to the Licensee for rights granted
-under the terms of this Agreement.
-
-"Licensed Software" shall mean specified product of commercially licensed
-version of Qt Software and/or QA Tools defined in Appendix 1 and/or Appendix 3,
-which Licensee has purchased and which is provided to Licensee under the terms
-of this Agreement. Licensed Software shall include corresponding online or
-electronic documentation, associated media and printed materials, including the
-source code (where applicable), example programs and the documentation.
-Licensed Software does not include Third Party Software (as defined in Section
-4) or Open Source Qt. The Qt Company may, in the course of its development
-activities, at its free and absolute discretion and without any obligation to
-send or publish any notifications to the Licensee or in general, make changes,
-additions or deletions in the components and functionalities of the Licensed
-Software, provided that no such changes, additions or deletions will affect
-the already released version of the Licensed Software, but only upcoming
-version(s).
-
-"Licensee" shall mean the individual or legal entity that is party to this
-Agreement.
-
-"Licensee's Records" shall mean books and records that contain information
-bearing on Licensee's compliance with this Agreement, Licensee's use of Open
-Source Qt and/or the payments due to The Qt Company under this Agreement,
-including, but not limited to user information, assembly logs, sales records
-and distribution records.
-
-"Modified Software" shall have the meaning as set forth in Section 2.3.
-
-"Online Services" shall mean any services or access to systems made available
-by The Qt Company to the Licensee over the Internet relating to the Licensed
-Software or for the purpose of use by the Licensee of the Licensed Software or
-Support. Use of any such Online Services is discretionary for the Licensee and
-some of them may be subject to additional fees.
-
-"Open Source Qt" shall mean Qt Software available under the terms of the GNU
-Lesser General Public License, version 2.1 or later ("LGPL") or the GNU General
-Public License, version 2.0 or later ("GPL"). For clarity, Open Source Qt shall
-not be provided, governed or used under this Agreement.
-
-"Party" or "Parties" shall mean Licensee and/or The Qt Company.
-
-"Permitted Software" shall mean (i) third party open source software products
-that are generally available for public in source code form and free of any
-charge under any of the licenses approved by Open Source Initiative as listed
-on https://opensource.org/licenses, which may include parts of Open Source Qt
-or be developed using Open Source Qt; and (ii) software The Qt Company has made
-available via its Qt Marketplace online distribution channel.
-
-"Pre-Release Code" shall have the meaning as set forth in Section 4.
-
-"Prohibited Combination" shall mean any effort to use, combine, incorporate,
-link or integrate Licensed Software with any software created with or
-incorporating Open Source Qt, or use Licensed Software for creation of any such
-software.
-
-"Purchase Order" shall have the meaning as set forth in Section 10.2.
-
-"QA Tools" shall mean software libraries and tools as defined in Appendix 1
-depending on which product(s) the Licensee has purchased under the Agreement.
-
-"Qt Software" shall mean the software libraries and tools of The Qt Company,
-which The Qt Company makes available under commercial and/or open source
-licenses.
-
-"Redistributables" shall mean the portions of the Licensed Software set forth
-in Appendix 1 that may be distributed pursuant to the terms of this Agreement
-in object code form only, including any relevant documentation. Where relevant,
-any reference to Licensed Software in this Agreement shall include and refer
-also to Redistributables.
-
-"Renewal Term" shall mean an extension of previous Development License Term as
-agreed between the Parties.
-
-"Submitted Modified Software" shall have the meaning as set forth in Section
-2.3.
-
-"Support" shall mean standard developer support that is provided by The Qt
-Company to assist Designated Users in using the Licensed Software in accordance
-with this Agreement and the Support Terms.
-
-"Support Terms" shall mean The Qt Company's standard support terms specified in
-Appendix 9 hereto.
-
-"Taxes" shall have the meaning set forth in Section 10.5.
-
-"The Qt Company" shall mean:
-    (i) in the event Licensee is an individual residing in the United States or
-        a legal entity incorporated in the United States or having its
-        headquarters in the United States, The Qt Company Inc., a Delaware
-        corporation with its office at 3031 Tisch Way, 110 Plaza West,
-        San Jose, CA 95128, USA.; or
-    (ii) in the event the Licensee is an individual residing outside of the
-        United States or a legal entity incorporated outside of the United
-        States or having its registered office outside of the United States,
-        The Qt Company Ltd., a Finnish company with its registered office at
-        Miestentie 7, 02150 Espoo, Finland.
-
-"Third-Party Software" shall have the meaning set forth in Section 4.
-
-"Updates" shall mean a release or version of the Licensed Software containing
-bug fixes, error corrections and other changes that are generally made
-available to users of the Licensed Software that have contracted for Support.
-Updates are generally depicted as a change to the digits following the decimal
-in the Licensed Software version number. The Qt Company shall make Updates
-available to the Licensee under the Support. Updates shall be considered as
-part of the Licensed Software hereunder.
-
-"Upgrades" shall mean a release or version of the Licensed Software containing
-enhancements and new features and are generally depicted as a change to the
-first digit of the Licensed Software version number. In the event Upgrades are
-provided to the Licensee under this Agreement, they shall be considered as part
-of the Licensed Software hereunder.
-
-2. OWNERSHIP
-
-2.1. Ownership of The Qt Company
-
-The Licensed Software is protected by copyright laws and international
-copyright treaties, as well as other intellectual property laws and treaties.
-The Licensed Software is licensed, not sold.
-
-All of The Qt Company's Intellectual Property Rights are and shall remain the
-exclusive property of The Qt Company or its licensors respectively. No rights
-to The Qt Company's Intellectual Property Rights are assigned or granted to
-Licensee under this Agreement, except when and to the extent expressly
-specified herein.
-
-2.2. Ownership of Licensee
-
-All the Licensee's Intellectual Property Rights are and shall remain the
-exclusive property of the Licensee or its licensors respectively.
-
-All Intellectual Property Rights to the Modified Software, Applications and
-Devices shall remain with the Licensee and no rights thereto shall be granted
-by the Licensee to The Qt Company under this Agreement (except as set forth in
-Section 2.3 below).
-
-2.3. Modified Software
-
-Licensee may create bug-fixes, error corrections, patches or modifications to
-the Licensed Software ("Modified Software"). Such Modified Software may break
-the source or binary compatibility with the Licensed Software (including
-without limitation through changing the application programming interfaces
-("API") or by adding, changing or deleting any variable, method, or class
-signature in the Licensed Software and/or any inter-process protocols,
-services or standards in the Licensed Software libraries). To the extent that
-Licensee's Modified Software so breaks source or binary compatibility with the
-Licensed Software, Licensee acknowledges that The Qt Company's ability to
-provide Support may be prevented or limited and Licensee's ability to make use
-of Updates may be restricted.
-
-Licensee may, at its sole and absolute discretion, choose to submit Modified
-Software to The Qt Company ("Submitted Modified Software") in connection with
-Licensee's Support request, service request or otherwise. In the event
-Licensee does so, then, Licensee hereby grants The Qt Company a sublicensable,
-assignable, irrevocable, perpetual, worldwide, non-exclusive, royalty-free and
-fully paid-up license, under all of Licensee's Intellectual Property Rights, to
-reproduce, adapt, translate, modify, and prepare derivative works of, publicly
-display, publicly perform, sublicense, make available and distribute such
-Submitted Modified Software as The Qt Company sees fit at its free and absolute
-discretion.
-
-3. LICENSES GRANTED
-
-3.1. Development with Licensed Software
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable license, valid for each Development
-License Term, to use, modify and copy the Licensed Software by Designated
-Users on the Development Platforms for the sole purposes of designing,
-developing, demonstrating and testing Application(s) and/or Devices, and to
-provide thereto related support and other related services to Customers. Each
-Application and/or Device can only include, incorporate or integrate
-contributions by such Designated Users who are duly licensed for the applicable
-Development Platform(s) and Deployment Platform(s) (i.e have a valid license
-for the appropriate Licensed Software product).
-
-Licensee may install copies of the Licensed Software on five (5) computers per
-Designated User, provided that only the Designated Users who have a valid
-Development License may use the Licensed Software.
-
-Licensee may at any time designate another Designated User to replace a
-then-current Designated User by notifying The Qt Company in writing, where such
-replacement is due to termination of employment, change of job duties, long
-time absence or other such permanent reason affecting Designated User's need
-for Licensed Software.
-
-Upon expiry of the initially agreed Development License Term, the respective
-Development License Term shall be automatically extended to one or more Renewal
-Term(s), unless and until either Party notifies the other Party in writing, or
-any other method acceptable to The Qt Company (it being specifically
-acknowledged and understood that verbal notification is explicitly deemed
-inadequate in all circumstances), that it does not wish to continue the
-Development License Term, such notification to be provided to the other Party
-no less than thirty (30) days before expiry of the respective Development
-License Term. The Qt Company shall, in good time before the due date for the
-above notification, remind the Licensee on the coming Renewal Term. Unless
-otherwise agreed between the Parties, Renewal Term shall be 12 months.
-
-Any such Renewal Term shall be subject to License Fees agreed between the
-Parties or, if no advance agreement exists, subject to The Qt Company's
-standard list pricing applicable at the commencement date of any such
-Renewal Term.
-
-The Qt Company may either request the Licensee to place a purchase order
-corresponding to a quote by The Qt Company, or use Licensee's stored Credit
-Card information in the Qt Account to automatically charge the Licensee for the
-relevant Renewal Term.
-
-3.2. Distribution of Applications
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable, revocable (for cause pursuant to
-this Agreement), right and license, valid for the Agreement Term, to
-    (i) distribute, by itself or through its Contractors, Redistributables as
-        installed, incorporated or integrated into Applications for execution
-        on the Deployment Platforms, and
-    (ii) grant perpetual and irrevocable sublicenses to Redistributables, as
-        distributed hereunder, for Customers solely to the extent necessary in
-        order for the Customers to use the Applications for their respective
-        intended purposes.
-
-Right to distribute the Redistributables as part of an Application as provided
-herein is not royalty-bearing but is conditional upon the Application having
-been created, updated and maintained under a valid and duly paid Development
-Licenses.
-
-3.3. Distribution of Devices
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable, revocable (for cause pursuant to
-this Agreement), right and license, valid for the Agreement Term, to
-    (i) distribute, by itself or through one or more tiers of Contractors,
-        Redistributables as installed, incorporated or integrated, or intended
-        to be installed, incorporated or integrated into Devices for execution
-        on the Deployment Platforms, and
-    (ii) grant perpetual and irrevocable sublicenses to Redistributables, as
-        distributed hereunder, for Customers solely to the extent necessary in
-        order for the Customers to use the Devices for their respective
-        intended purposes.
-
-Right to distribute the Devices as provided herein is conditional upon
-    (i) the Devices having been created, updated and maintained under a valid
-        and duly paid Development Licenses, and
-    (ii) the Licensee having acquired corresponding Distribution Licenses at
-        the time of distribution of any Devices to Customers.
-
-3.4. Further Requirements
-
-The licenses granted above in this Section 3 by The Qt Company to Licensee are
-conditional and subject to Licensee's compliance with the following terms:
-    (i) Licensee acknowledges that The Qt Company has separate products of
-        Licensed Software for the purpose of Applications and Devices
-        respectively, where development and distribution of Devices is only
-        allowed using the correct designated product. Licensee shall make sure
-        and bear the burden of proof that Licensee is using a correct product
-        of Licensed Software entitling Licensee to development and distribution
-        of Devices;
-    (ii) Licensee shall not remove or alter any copyright, trademark or other
-        proprietary rights notice(s) contained in any portion of the Licensed
-        Software;
-    (iii) Applications must add primary and substantial functionality to the
-        Licensed Software so as not to compete with the Licensed Software;
-    (iv) Applications may not pass on functionality which in any way makes it
-        possible for others to create software with the Licensed Software;
-        provided however that Licensee may use the Licensed Software's
-        scripting and QML ("Qt Quick") functionality solely in order to enable
-        scripting, themes and styles that augment the functionality and
-        appearance of the Application(s) without adding primary and substantial
-        functionality to the Application(s);
-    (v) Licensee shall not use Licensed Software in any manner or for any
-        purpose that infringes, misappropriates or otherwise violates any
-        Intellectual property or right of any third party, or that violates any
-        applicable law;
-    (vi) Licensee shall not use The Qt Company's or any of its suppliers'
-        names, logos, or trademarks to market Applications, except that
-        Licensee may use "Built with Qt" logo to indicate that Application(s)
-        or Device(s) was developed using the Licensed Software;
-    (vii) Licensee shall not distribute, sublicense or disclose source code of
-        Licensed Software to any third party (provided however that Licensee
-        may appoint employee(s) of Contractors and Affiliates as Designated
-        Users to use Licensed Software pursuant to this Agreement). Such right
-        may be available for the Licensee subject to a separate software
-        development kit ("SDK") license agreement to be concluded with The Qt
-        Company;
-    (viii) Licensee shall not grant the Customers a right to (a) make copies of
-        the Redistributables except when and to the extent required to use the
-        Applications and/or Devices for their intended purpose, (b) modify the
-        Redistributables or create derivative works thereof, (c) decompile,
-        disassemble or otherwise reverse engineer Redistributables, or (d)
-        redistribute any copy or portion of the Redistributables to any third
-        party, except as part of the onward sale of the Application or Device
-        on which the Redistributables are installed;
-    (ix) Licensee shall not and shall cause that its Affiliates or Contractors
-        shall not use Licensed Software in any Prohibited Combination, unless
-        Licensee has received an advance written permission from The Qt Company
-        to do so. Absent such written permission, any and all distribution by
-        the Licensee during the Agreement Term of a hardware device or product
-        a) which incorporate or integrate any part of Licensed Software or Open
-        Source Qt; or b) where substantial functionality is provided by
-        software built with Licensed Software or Open Source Qt or otherwise
-        depends on the Licensed Software or Open Source Qt, shall be considered
-        to be Device distribution under this Agreement and shall be dependent
-        on Licensee's compliance thereof (including but not limited to
-        obligation to pay applicable License Fees for such distribution).
-        Notwithstanding what is provided above in this sub-section (ix),
-        Licensee is entitled to use and combine Licensed Software with any
-        Permitted Software;
-    (x) Licensee shall cause all of its Affiliates, Contractors and Customers
-        entitled to make use of the licenses granted under this Agreement, to
-        be contractually bound to comply with the relevant terms of this
-        Agreement and not to use the Licensed Software beyond the terms hereof
-        and for any purposes other than operating within the scope of their
-        services for Licensee. Licensee shall be responsible for any and all
-        actions and omissions of its Affiliates and Contractors relating to the
-        Licensed Software and use thereof (including but not limited to payment
-        of all applicable License Fees);
-    (xi) Except when and to the extent explicitly provided in this Section 3,
-        Licensee shall not transfer, publish, disclose, display or otherwise
-        make available the Licensed Software; and
-    (xii) Licensee shall not attempt or enlist a third party to conduct or
-        attempt to conduct any of the above.
-
-Above terms shall not be applicable if and to the extent they conflict with any
-mandatory provisions of any applicable laws.
-
-Any use of Licensed Software beyond the provisions of this Agreement is
-strictly prohibited and requires an additional license from The Qt Company.
-
-3.5 QA Tools License
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable license, valid for the Development
-License Term, to use the QA Tools for Licensee's internal business purposes in
-the manner provided below and in Appendix 1 hereto.
-
-Licensee may modify the QA Tools except for altering or removing any details of
-ownership, copyright, trademark or other property right connected with the QA
-Tools.
-
-Licensee shall not distribute the QA Tools or any part thereof, modified or
-unmodified, separately or as part of any software package, Application or
-Device.
-
-Upon expiry of the initially agreed Development License Term, the respective
-Development License Term shall be automatically extended to one or more Renewal
-Term(s), unless and until either Party notifies the other Party in writing, or
-any other method acceptable to The Qt Company (it being specifically
-acknowledged and understood that verbal notification is explicitly deemed
-inadequate in all circumstances), that it does not wish to continue the
-Development License Term, such notification to be provided to the other Party
-no less than thirty (30) days before expiry of the respective Development
-License Term. The Qt Company shall, in good time before the due date for the
-above notification, remind the Licensee on the coming Renewal Term. Unless
-otherwise agreed between the Parties, Renewal Term shall be 12 months.
-
-Any such Renewal Term shall be subject to License Fees agreed between the
-Parties or, if no advance agreement exists, subject to The Qt Company's
-standard list pricing applicable at the commencement date of any such
-Renewal Term.
-
-3.6 Evaluation License
-
-Subject to the terms of this Agreement, The Qt Company grants to Licensee a
-worldwide, non-exclusive, non-transferable license, valid for the Evaluation
-License Term to use the Licensed Software solely for the Licensee's internal
-use to evaluate and determine whether the Licensed Software meets Licensee's
-business requirements, specifically excluding any commercial use of the
-Licensed Software or any derived work thereof.
-
-Upon the expiry of the Evaluation License Term, Licensee must either
-discontinue use of the relevant Licensed Software or acquire a commercial
-Development License or QA Tools License specified herein.
-
-4. THIRD-PARTY SOFTWARE
-
-The Licensed Software may provide links or access to third party libraries or
-code (collectively "Third-Party Software") to implement various functions.
-Third-Party Software does not, however, comprise part of the Licensed Software,
-but is provided to Licensee complimentary and use thereof is discretionary for
-the Licensee. Third-Party Software will be listed in the ".../src/3rdparty"
-source tree delivered with the Licensed Software or documented in the Licensed
-Software, as such may be amended from time to time. Licensee acknowledges that
-use or distribution of Third-Party Software is in all respects subject to
-applicable license terms of applicable third-party right holders.
-
-5. PRE-RELEASE CODE
-
-The Licensed Software may contain pre-release code and functionality, or sample
-code marked or otherwise stated with appropriate designation such as
-"Technology Preview", "Alpha", "Beta", "Sample", "Example" etc.
-("Pre-Release Code").
-
-Such Pre-Release Code may be present complimentary for the Licensee, in order
-to provide experimental support or information for new platforms or
-preliminary versions of one or more new functionalities or for other similar
-reasons. The Pre-Release Code may not be at the level of performance and
-compatibility of a final, generally available, product offering.  The
-Pre-Release Code may not operate correctly, may contain errors and may be
-substantially modified by The Qt Company prior to the first commercial
-product release, if any. The Qt Company is under no obligation to make
-Pre-Release Code commercially available, or provide any Support or Updates
-relating thereto. The Qt Company assumes no liability whatsoever regarding
-any Pre-Release Code, but any use thereof is exclusively at Licensee's own risk
-and expense.
-
-For clarity, unless Licensed Software specifies different license terms for the
-respective Pre-Release Code, the Licensee is entitled to use such pre-release
-code pursuant to Section 3, just like other Licensed Software.
-
-6. LIMITED WARRANTY AND WARRANTY DISCLAIMER
-
-The Qt Company hereby represents and warrants that (i) it has the power and
-authority to grant the rights and licenses granted to Licensee under this
-Agreement, and (ii) Licensed Software will operate materially in accordance
-with its specifications.
-
-Except as set forth above, the Licensed Software is licensed to Licensee "as
-is" and Licensee's exclusive remedy and The Qt Company's entire liability for
-errors in the Licensed Software shall be limited, at The Qt Company's option,
-to correction of the error, replacement of the Licensed Software or return of
-the applicable fees paid for the defective Licensed Software for the time
-period during which the License is not able to utilize the Licensed Software
-under the terms of this Agreement.
-
-TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF
-ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER
-WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
-NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT
-WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE'S REQUIREMENTS OR THAT
-IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE
-UNINTERRUPTED.
-
-7. LIMITATION OF LIABILITY
-
-EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II)
-BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO
-EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT,
-LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL,
-CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND,
-HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT.
-
-EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II)
-BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO
-EVENT SHALL EITHER PARTY'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT
-EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT COMPANY BY LICENSEE
-DURING THE DEVELOPMENT LICENSE TERM DURING WHICH THE EVENT RESULTING IN SUCH
-LIABILITY OCCURRED.
-
-THE PROVISIONS OF THIS SECTION 7 ALLOCATE THE RISKS UNDER THIS AGREEMENT
-BETWEEN THE QT COMPANY AND LICENSEE AND THE PARTIES HAVE RELIED UPON THE
-LIMITATIONS SET FORTH HEREIN IN DETERMINING WHETHER TO ENTER INTO THIS
-AGREEMENT.
-
-NOTWITHSTANDING ANYTHING TO THE CONTRARY IN THIS AGREEMENT, LICENSEE SHALL
-ALWAYS BE LIABLE TO PAY THE APPLICABLE LICENSE FEES CORRESPONDING TO ITS
-ACTUAL USE OF LICENSED SOFTWARE.
-
-8. SUPPORT, UPDATES AND ONLINE SERVICES
-
-Upon due payment of the agreed License Fees the Licensee will be eligible to
-receive Support and Updates and to use the Online Services during the agreed
-Development License Term or other agreed fixed time period. Support is
-provided according to agreed support level and subject to applicable
-requirements and restrictions, as specified in the Support Terms.
-
-Unless otherwise decided by The Qt Company at its free and absolute discretion,
-Upgrades will not be included in the Support but may be available subject to
-additional fees.
-
-From time to time The Qt Company may change the Support Terms, provided that
-during the respective ongoing Support period the level of Support may not be
-reduced without the consent of the Licensee.
-
-Unless otherwise agreed, The Qt Company shall not be responsible for providing
-any service or support to Customers.
-
-9. CONFIDENTIALITY
-
-Each Party acknowledges that during the Agreement Term each Party may receive
-information about the other Party's business, business methods, business plans,
-customers, business relations, technology, and other information, including the
-terms of this Agreement, that is confidential and of great value to the other
-Party, and the value of which would be significantly reduced if disclosed to
-third parties ("Confidential Information"). Accordingly, when a Party (the
-"Receiving Party") receives Confidential Information from the other Party (the
-"Disclosing Party"), the Receiving Party shall only disclose such information
-to employees and Contractors on a need to know basis, and shall cause its
-employees and employees of its Affiliates to: (i) maintain any and all
-Confidential Information in confidence; (ii) not disclose the Confidential
-Information to a third party without the Disclosing Party's prior written
-approval; and (iii) not, directly or indirectly, use the Confidential
-Information for any purpose other than for exercising its rights and
-fulfilling its responsibilities pursuant to this Agreement. Each Party shall
-take reasonable measures to protect the Confidential Information of the other
-Party, which measures shall not be less than the measures taken by such Party
-to protect its own confidential and proprietary information.
-
-Obligation of confidentiality shall not apply to information that (i) is or
-becomes generally known to the public through no act or omission of the
-Receiving Party; (ii) was in the Receiving Party's lawful possession prior to
-the disclosure hereunder and was not subject to limitations on disclosure or
-use; (iii) is developed independently by employees or Contractors of the
-Receiving Party or other persons working for the Receiving Party who have not
-had access to the Confidential Information of the Disclosing Party, as proven
-by the written records of the Receiving Party; (iv) is lawfully disclosed to
-the Receiving Party without restrictions, by a third party not under an
-obligation of confidentiality; or (v) the Receiving Party is legally compelled
-to disclose, in which case the Receiving Party shall notify the Disclosing
-Party of such compelled disclosure and assert the privileged and confidential
-nature of the information and cooperate fully with the Disclosing Party to
-limit the scope of disclosure and the dissemination of disclosed Confidential
-Information to the minimum extent necessary.
-
-The obligations under this Section 9 shall continue to remain in force for a
-period of five (5) years after the last disclosure, and, with respect to trade
-secrets, for so long as such trade secrets are protected under applicable trade
-secret laws.
-
-10. FEES, DELIVERY AND PAYMENT
-
-10.1. License Fees
-
-License Fees are described in The Qt Company's standard price list, quote or
-Purchase Order confirmation or in an Appendix 2 hereto, as the case may be.
-
-Unless otherwise expressly provided in this Agreement, the License Fees shall
-not be refunded or claimed as a credit in any event or for any reason
-whatsoever.
-
-10.2. Ordering Licenses
-
-Licensee may purchase Development Licenses, Distribution Licenses and QA Tools
-Licenses pursuant to agreed pricing terms or, if no specific pricing terms have
-been agreed upon, at The Qt Company's standard pricing terms applicable at the
-time of purchase.
-
-Unless expressly otherwise agreed, any price or other term quoted to the
-Licensee or specified herein shall only be valid for the thirty (30) days from
-the effective date of this Agreement, Appendix 2 or the date of the quote, as
-applicable.
-
-Licensee shall submit all purchase orders for Development Licenses and
-Distribution Licenses to The Qt Company by email or any other method acceptable
-to The Qt Company (each such order is referred to herein as a "Purchase Order")
-for confirmation, whereupon the Purchase Order shall become binding between the
-Parties.
-
-Licensee acknowledges and agrees that all Purchase Orders for Licensed Software
-the Licensee makes during the Agreement Term shall be governed exclusively
-under the terms of this Agreement.
-
-10.3. Distribution License Packs
-
-Unless otherwise agreed, Distribution Licenses shall be purchased by way of
-Distribution License Packs.
-
-Upon due payment of the ordered Distribution License Pack(s), the Licensee will
-have an account of Distribution Licenses available for distributing the
-Redistributables in accordance with this Agreement.
-
-Each time Licensee distributes a copy of Redistributables, then one
-Distribution License is used, and Licensee's account of available Distribution
-Licenses is decreased accordingly.
-
-Licensee may distribute copies of the Redistributables so long as Licensee has
-Distribution Licenses remaining on its account.
-
-10.4. Payment Terms
-
-License Fees and any other charges under this Agreement shall be paid by
-Licensee no later than thirty (30) days from the date of the applicable invoice
-from The Qt Company.
-
-The Qt Company will submit an invoice to Licensee after the date of this
-Agreement and/or after The Qt Company receives a Purchase Order from Licensee.
-
-A late payment charge of the lower of (a) one percent per month; or (b) the
-interest rate stipulated by applicable law, shall be charged on any unpaid
-balances that remain past due and which have not been disputed by the Licensee
-in good faith.
-
-10.5. Taxes
-
-All License Fees and other charges payable hereunder are gross amounts but
-exclusive of any value added tax, use tax, sales tax, withholding tax and other
-taxes, duties or tariffs ("Taxes") levied directly for the sale, delivery or
-use of Licensed Software hereunder pursuant to any applicable law. Such
-applicable Taxes shall be paid by Licensee to The Qt Company, or, where
-applicable, in lieu of payment of such Taxes to The Qt Company, Licensee shall
-provide an exemption certificate to The Qt Company and any applicable
-authority.
-
-11. RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS
-
-11.1. Licensee's Record-keeping
-
-Licensee shall at all times during the Agreement Term and for a period of two
-(2) years thereafter maintain Licensee's Records in an accurate and up-to-date
-form. Licensee's Records shall be adequate to reasonably enable The Qt Company
-to determine Licensee's compliance with the provisions of this Agreement. The
-records shall conform to general good accounting practices.
-
-Licensee shall, within thirty (30) days from receiving The Qt Company's request
-to that effect, deliver to The Qt Company a report based on Licensee's Records,
-such report to contain information, in sufficient detail, on (i) number and
-identity of users working with Licensed Software or Open Source Qt, (ii) copies
-of Redistributables distributed by Licensee during the most recent calendar
-quarter and/or any other term specified by The Qt Company, , and (iii) any
-other information pertaining to Licensee's compliance with the terms of this
-Agreement (like e.g. information on products and/or projects relating to use of
-Distribution Licenses), as The Qt Company may reasonably require from time to
-time.
-
-11.2. The Qt Company's Audit Rights
-
-The Qt Company or an independent auditor acting on behalf of The Qt Company's,
-may, upon at least thirty (30) days' prior written notice and at its expense,
-audit Licensee with respect to the Licensee's use of the Licensed Software, but
-not more frequently than once during each 6-month period. Such audit may be
-conducted by mail, electronic means or through an in-person visit to Licensee's
-place of business. Any possible in-person audit shall be conducted during
-regular business hours at Licensee's facilities and shall not unreasonably
-interfere with Licensee's business activities and shall be limited in scope to
-verify Licensee's compliance with the terms of this Agreement. The Qt Company
-or the independent auditor acting on behalf of The Qt Company shall be entitled
-to inspect Licensee's Records and conduct necessary interviews of Licensee's
-relevant employees and Contractors. All such Licensee's Records and use thereof
-shall be subject to an obligation of confidentiality under this Agreement.
-
-If an audit reveals that Licensee is using the Licensed Software beyond scope
-of the licenses Licensee has paid for, Licensee shall pay to The Qt Company any
-amounts owed for such unauthorized use within 30 days from receipt of the
-corresponding invoice from The Qt Company.
-
-In addition, in the event the audit reveals a material violation of the terms
-of this Agreement (without limitation, either (i) underpayment of more than 10
-% of License Fees or 10,000 euros (whichever is more) or (ii) distribution of
-products, which include or result from Prohibited Combination, shall be deemed
-a material violation for purposes of this section), then the Licensee shall
-pay The Qt Company's reasonable cost of conducting such audit.
-
-12. TERM AND TERMINATION
-
-12.1. Agreement Term
-
-This Agreement shall enter into force upon due acceptance by both Parties and
-remain in force until terminated pursuant to the terms of this Section 12
-("Agreement Term").
-
-12.2. Termination for breach and suspension of rights
-Either Party shall have the right to terminate this Agreement upon thirty (30)
-days prior written notice if the other Party commits a material breach of any
-obligation of this Agreement and fails to remedy such breach within such notice
-period.
-
-Instead of termination, The Qt Company shall have the right to suspend or
-withhold grants of all rights to the Licensed Software hereunder, including but
-not limited to the Development Licenses, Distribution License, and Support,
-should Licensee fail to make payment in timely fashion or otherwise violates or
-is reasonably suspected to violate its obligations or terms of this Agreement,
-and where such violation or breach is not cured within ten (10) business days
-following The Qt Company's written notice thereof.
-
-12.3. Termination for insolvency
-
-Either Party shall have the right to terminate this Agreement immediately upon
-written notice in the event that the other Party becomes insolvent, files for
-any form of bankruptcy, makes any assignment for the benefit of creditors, has
-a receiver, administrative receiver or officer appointed over the whole or a
-substantial part of its assets, ceases to conduct business, or an act
-equivalent to any of the above occurs under the laws of the jurisdiction of the
-other Party.
-
-12.4. Parties' Rights and Duties upon Termination
-
-Upon expiry or termination of the Agreement, Licensee shall cease and shall
-cause all Designated Users (including those of its Affiliates' and
-Contractors') to cease using the Licensed Software under this Agreement. For
-clarity, a Development License of a Designated User or a QA Tools License, and
-all rights relating thereto, shall always terminate at the expiry of the
-respective Development License Term, even if the Agreement continues to remain
-in force.
-
-Upon such termination the Licensee shall destroy or return to The Qt Company
-all copies of the Licensed Software and all related materials and will certify
-the same by Licensee's duly authorized officer to The Qt Company upon its
-request, provided however that Licensee may retain and exploit such copies of
-the Licensed Software as it may reasonably require in providing continued
-support to Customers.
-
-Except when this Agreement is terminated by The Qt Company due to Licensee's
-material breach as set forth in Section 12.2, the Licensee may continue
-distribution of Applications and Devices under the terms of this Agreement
-despite the termination of this Agreement. In such event the terms hereof will
-continue to be applicable and govern any such distribution of Applications and
-Devices beyond the expiry or termination of this Agreement. In case of
-termination by The Qt Company due to Licensee's material breach, Licensee must
-cease any distribution of Applications and Devices at the date of termination
-of this Agreement.
-
-Expiry or termination of this Agreement for any reason whatsoever shall not
-relieve Licensee of its obligation to pay any License Fees accrued or payable
-to The Qt Company prior to the effective date of termination, and Licensee pay
-to The Qt Company all such fees within 30 days from the effective date of
-termination of this Agreement.
-
-Termination of this Agreement shall not affect any rights of Customers to
-continue use of Applications and Devices (and therein incorporated
-Redistributables).
-
-12.5. Extension of Rights under Special Circumstances
-
-In the event of The Qt Company choosing not to renew the Development License(s)
-or QA Tools Licenses, as set forth in Section 3.1 and 3.5 respectively, and
-where such decision of non-renewal is not due to any ongoing breach or alleged
-breach (as reasonably determined by The Qt Company) by Licensee of the terms of
-this Agreement or any applicable license terms of Open Source Qt, then all
-valid and affected Development Licenses and QA Tools licenses possessed by the
-Licensee at such date shall be extended to be valid in perpetuity under the
-terms of this Agreement and Licensee is entitled to purchase additional
-licenses as set forth in Section 10.2.
-
-In the event The Qt Company is declared bankrupt under a final, non-cancellable
-decision by relevant court of law, and this Agreement is not, at the date of
-expiry of the Development License(s) or QA Tools Licenses, assigned to party,
-who has assumed The Qt Company's position as a legitimate licensor of Licensed
-Software under this Agreement, then all valid Development Licenses and QA Tools
-Licenses possessed by the Licensee at such date of expiry, and which the
-Licensee has not notified for expiry, shall be extended to be valid in
-perpetuity under the terms of this Agreement.
-
-For clarity, in case of an extension under this Section 12.5, any such
-extension shall not apply to The Qt Company's Support obligations, but Support
-shall be provided only up until the end of the respective fixed Development
-License Term regardless of the extension of relevant Development License or QA
-Tools License, unless otherwise agreed between the Parties.
-
-13. GOVERNING LAW AND LEGAL VENUE
-
-In the event this Agreement is in the name of The Qt Company Inc., a Delaware
-Corporation, then:
-    (i) this Agreement shall be construed and interpreted in accordance with
-        the laws of the State of California, USA, excluding its choice of law
-        provisions;
-    (ii) the United Nations Convention on Contracts for the International Sale
-        of Goods will not apply to this Agreement; and
-    (iii) any dispute, claim or controversy arising out of or relating to this
-        Agreement or the breach, termination, enforcement, interpretation or
-        validity thereof, including the determination of the scope or
-        applicability of this Agreement to arbitrate, shall be determined by
-        arbitration in San Francisco, USA, before one arbitrator. The
-        arbitration shall be administered by JAMS pursuant to JAMS' Streamlined
-        Arbitration Rules and Procedures. Judgment on the Award may be entered
-        in any court having jurisdiction. This Section shall not preclude
-        parties from seeking provisional remedies in aid of arbitration from a
-        court of appropriate jurisdiction.
-
-In the event this Agreement is in the name of The Qt Company Ltd., a Finnish
-Company, then:
-    (i) this Agreement shall be construed and interpreted in accordance with
-        the laws of Finland, excluding its choice of law provisions;
-    (ii) the United Nations Convention on Contracts for the International Sale
-        of Goods will not apply to this Agreement; and
-    (iii) any disputes, controversy or claim arising out of or relating to this
-        Agreement, or the breach, termination or validity thereof shall be
-        finally settled by arbitration in accordance with the Arbitration Rules
-        of International Chamber of Commerce. The arbitration tribunal shall
-        consist of one (1), or if either Party so requires, of three (3),
-        arbitrators. The award shall be final and binding and enforceable in
-        any court of competent jurisdiction. The arbitration shall be held in
-        Helsinki, Finland and the process shall be conducted in the English
-        language. This Section shall not preclude parties from seeking
-        provisional remedies in aid of arbitration from a court of appropriate
-        jurisdiction.
-
-14. GENERAL PROVISIONS
-
-14.1. No Assignment
-
-Except in the case of a merger or sale of substantially all of its corporate
-assets, Licensee shall not be entitled to assign or transfer all or any of its
-rights, benefits and obligations under this Agreement without the prior written
-consent of The Qt Company, which shall not be unreasonably withheld or delayed.
-The Qt Company shall be entitled to freely assign or transfer any of its
-rights, benefits or obligations under this Agreement.
-
-14.2. No Third-Party Representations
-
-Licensee shall make no representations or warranties concerning the Licensed
-Software on behalf of The Qt Company. Any representation or warranty Licensee
-makes or purports to make on The Qt Company's behalf shall be void as to
-The Qt Company.
-
-14.3. Surviving Sections
-
-Any terms and conditions that by their nature or otherwise reasonably should
-survive termination of this Agreement shall so be deemed to survive. Such
-sections include especially the following: 1, 2, 6, 7, 9, 11, 12.4, 13 and 14.
-
-14.4. Entire Agreement
-
-This Agreement, the Appendices hereto, the License Certificate and any
-applicable quote and Purchase Order accepted by The Qt Company constitute the
-complete agreement between the Parties and supersedes all prior or
-contemporaneous discussions, representations, and proposals, written or oral,
-with respect to the subject matters discussed herein.
-
-In the event of any conflict or inconsistency between this Agreement and any
-Purchase Order, the terms of this Agreement will prevail over the terms of the
-Purchase Order with respect to such conflict or inconsistency.
-
-Parties specifically acknowledge and agree that this Agreement prevails over
-any click-to-accept or similar agreements the Designated Users may need to
-accept online upon download of the Licensed Software, as may be required by
-The Qt Company's applicable processes relating to Licensed Software.
-
-14.5. Modifications
-
-No modification of this Agreement shall be effective unless contained in a
-writing executed by an authorized representative of each Party. No term or
-condition contained in Licensee's Purchase Order ("Deviating Terms") shall
-apply unless The Qt Company has expressly agreed such Deviating Terms in
-writing. Unless and to the extent expressly agreed by The Qt Company, any such
-Deviating Terms shall be deemed void and with no legal effect. For clarity,
-delivery of the Licensed Software following the receipt of the Purchase Order
-including Deviating Terms shall not constitute acceptance of such Deviating
-Terms.
-
-14.6. Force Majeure
-
-Except for the payment obligations hereunder, neither Party shall be liable to
-the other for any delay or non-performance of its obligations hereunder in the
-event and to the extent that such delay or non-performance is due to an event
-of act of God, terrorist attack or other similar unforeseeable catastrophic
-event that prevents either Party for fulfilling its obligations under this
-Agreement and which such Party cannot avoid or circumvent ("Force Majeure
-Event"). If the Force Majeure Event results in a delay or non-performance of a
-Party for a period of three (3) months or longer, then either Party shall have
-the right to terminate this Agreement with immediate effect without any
-liability (except for the obligations of payment arising prior to the event of
-Force Majeure) towards the other Party.
-
-14.7. Notices
-
-Any notice given by one Party to the other shall be deemed properly given and
-deemed received if specifically acknowledged by the receiving Party in writing
-or when successfully delivered to the recipient by hand, fax, or special
-courier during normal business hours on a business day to the addresses
-specified for each Party on the signature page. Each communication and document
-made or delivered by one Party to the other Party pursuant to this Agreement
-shall be in the English language.
-
-14.8. Export Control
-
-Licensee acknowledges that the Redistributables, as incorporated in
-Applications or Devices, may be subject to export control restrictions under
-the applicable laws of respective countries. Licensee shall fully comply with
-all applicable export license restrictions and requirements as well as with all
-laws and regulations relating to the Redistributables and exercise of licenses
-hereunder and shall procure all necessary governmental authorizations,
-including without limitation, all necessary licenses, approvals, permissions or
-consents, where necessary for the re-exportation of the Redistributables,
-Applications and/or Devices.
-
-14.9. No Implied License
-
-There are no implied licenses or other implied rights granted under this
-Agreement, and all rights, save for those expressly granted hereunder, shall
-remain with The Qt Company and its licensors. In addition, no licenses or
-immunities are granted to the combination of the Licensed Software with any
-other software or hardware not delivered by The Qt Company under this
-Agreement.
-
-14.10. Attorney Fees
-
-The prevailing Party in any action to enforce this Agreement shall be entitled
-to recover its attorney's fees and costs in connection with such action, as to
-be ordered by the relevant dispute resolution body.
-
-14.11. Privacy
-
-Licensee acknowledges and agrees that for the purpose of this Agreement,
-The Qt Company may collect, use, transfer and disclose personal data pertaining
-to Designated Users as well as any other employees and directors of the
-Licensee and its Contractors relevant for carrying out the intent of this
-Agreement. Such personal data will be primarily collected from the relevant
-individuals but may be collected also from Licensee (e.g. in the course of
-Licensee's reporting obligations). The Parties acknowledge that as
-The Qt Company determines the purpose and means for such collection and
-processing of the applicable personal data, The Qt Company shall be regarded as
-the Data Controller under the applicable Data Protection Legislation.
-The Qt Company shall process any such personal data in accordance with its
-privacy and security policies and practices, which will comply with all
-applicable requirements of the Data Protection Legislation.
-
-14.12. Severability
-
-If any provision of this Agreement shall be adjudged by any court of competent
-jurisdiction to be unenforceable or invalid, that provision shall be limited or
-eliminated to the minimum extent necessary so that this Agreement shall
-otherwise remain in full force and effect and enforceable.
-
-14.13. Marketing Rights
-
-Parties have agreed upon Marketing Rights pursuant to Appendix 7, if any.
-
-
-
-
-APPENDICES
-The Agreement includes following Appendices 1-10, as applicable.
-- Appendix 1: Licensed Software details
-- Appendix 2: Pricing
-- Appendix 3: Add-on Software details (optional)
-- Appendix 4: Small business and startup Licenses (optional)
-- Appendix 5: Non-commercial and educational Licenses (optional)
-- Appendix 6: License Reporting (optional)
-- Appendix 7: Marketing Rights (optional)
-- Appendix 8: Intentionally left blank (optional)
-- Appendix 9: Support Terms
-- Appendix 10: Conversion from legacy Licenses to Subscription (optional)
-- Appendix 11: TERMS OF USE - QT INSIGHT TRACKER LIBRARY
-
-
-APPENDIX 1: LICENSED SOFTWARE
-
-The modules and/or tools that are included in the latest publicly available
-version of the respective product at the effective date of this Agreement- Qt
-for Application Development Professional (ADP), Qt for Application Development
-Enterprise (ADE), Qt for Device Creation Professional (DCP), Qt for Device
-Creation Enterprise (DCE), - are marked with "X" in the below table. The
-modules and tools are specific to each product version respectively and may
-vary from version to version. Modules and tools included in the latest publicly
-available version of the respective product at any given time are listed in
-Appendix 1 of the latest version of this Agreement available at
-www.qt.io/terms-conditions/. If a new version of Licensed Software does not
-include a module or tool present in an older version which Licensee is entitled
-to use under a valid license from The Qt Company, then Licensee will continue
-to have such right during the Term of this Agreement. In the event a new
-version of the Licensed Software adds modules or tools to any previous
-version(s), Licensee's rights will extend to cover also such additional modules
-and tools.
-
-Parts of the product that are permitted for distribution in object-code form
-only ("Redistributables") are marked with "R" in the below table.
-
-+----------------------------------------------------------+
-| Modules / Tools                  | ADP | ADE | DCP | DCE |
-+----------------------------------------------------------+
-| Active Qt                        | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt 3D                            | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt 5 Core Compatibility APIs     | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Android Extras                | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Bluetooth                     | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Canvas 3D                     | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Charts                        | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Concurrent                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Core                          | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Data Visualization            | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt D-Bus                         | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt for Python                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt for WebAssembly               | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Gamepad                       | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Graphical Effects             | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt GUI                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Help                          | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Image Formats                 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Location                      | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Lottie Animation              | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Mac Extras                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Multimedia                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Multimedia Widgets            | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Network                       | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Network Authorization         | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt NFC                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt OpenGL                        | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt PDF                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Platform Headers              | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Positioning                   | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Print Support                 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Purchasing                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt QML                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick                         | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick 3D                      | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Controls 1              | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Controls                | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Dialogs                 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Extras                  | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Layouts                 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Test                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Timeline                | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick WebGL                   | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Widgets                 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Remote Objects                | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Script                        | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Script Tools                  | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt SCXML                         | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Sensors                       | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Serial Bus                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Serial Port                   | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Shader Tools                  | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Speech                        | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt State Machine                 | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt SQL                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt SVG                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Test                          | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt UI Tools                      | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Virtual Keyboard              | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Wayland Compositor            | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebChannel                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebEngine                     | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebSockets                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt WebView                       | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Widgets                       | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Windows Extras                | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt X11 Extras                    | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt XML                           | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt XML Patterns                  | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Designer (Qt Widget Designer) | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Linguist                      | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Assistant                     | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| lupdate                          | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| lrelease                         | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| lconvert                         | X,R | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt MQTT                          |     | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt KNX                           |     | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt OPC UA                        |     | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Qt CoAP                          |     | X,R | X,R | X,R |
-+----------------------------------------------------------+
-| Boot 2 Qt stacks                 |     |     | X,R | X,R |
-+----------------------------------------------------------+
-| Qt OTA                           |     |     | X,R | X,R |
-+----------------------------------------------------------+
-| Device Utilities                 |     |     | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Debugging Bridge (QDB) Daemon |     |     | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Ultralite Controls      |     |     | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Quick Ultralite               |     |     | X,R | X,R |
-+----------------------------------------------------------+
-| Qt Safe Renderer (QSR)           |     |     |     | X,R |
-+----------------------------------------------------------+
-| Qt Application Manager           |     |     |     | X,R |
-+----------------------------------------------------------+
-| Qt Interface Framework           |     |     |     | X,R |
-+----------------------------------------------------------+
-| Neptune Reference UI             |     |     |     | X,R |
-+----------------------------------------------------------+
-| Qt for Android Automotive (QAA)  |     |     |     | X,R |
-+----------------------------------------------------------+
-| Qt Creator                       |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| Qt Design Studio Professional    |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| androiddeployqt                  |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| androidtestrunner                |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| canbusutil                       |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| dumpcpp                          |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| dumpdoc                          |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| fixqt4headers.pl                 |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| idc                              |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| moc                              |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| pixeltool                        |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qdbus                            |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qdbuscpp2xml                     |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qdbusviwer                       |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qdbusxml2cpp                     |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qdistancefieldgenerator          |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qdoc                             |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qhelpgenerator                   |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qlalr                            |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmake                            |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qml                              |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmlcachegen                      |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmldom                           |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmleasing                        |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmlformat                        |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmllint                          |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmlpreview                       |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmlprofiler                      |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmlscene                         |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmltestrunner                    |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmltime                          |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qmlviewer                        |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qtdiag                           |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qtpaths                          |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qtplugininfo                     |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| qvkgen                           |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| rcc                              |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| tracegen                         |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| uic                              |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| windeployqt                      |  X  |  X  |  X  |  X  |
-+----------------------------------------------------------+
-| Target toolchains                |     |     |  X  |  X  |
-+----------------------------------------------------------+
-| Qt Debugging Bridge Host Tools   |     |     |  X  |  X  |
-+----------------------------------------------------------+
-| qtconfig-gui                     |     |     |  X  |  X  |
-+----------------------------------------------------------+
-| Qt Emulator                      |     |     |  X  |  X  |
-+----------------------------------------------------------+
-| Qt Creator VxWorks plugin        |     |     |  X  |  X  |
-+----------------------------------------------------------+
-| Qt Creator plugin for Qt         |     |     |     |  X  |
-| Application Manager              |     |     |     |     |
-+----------------------------------------------------------+
-| qmlinterfacegenerator            |     |     |     |  X  |
-+----------------------------------------------------------+
-| qmltocpp                         |     |     |     |  X  |
-+----------------------------------------------------------+
-| qulfontcompiler                  |     |     |     |  X  |
-+----------------------------------------------------------+
-| Qt Deployment Server             |     |     |     |  X  |
-+----------------------------------------------------------+
-
-
-Rights for Application and Device use cases
-
-Following table summarizes the rights afforded by different products of the
-Licensed Software to create and distribute Applications and Devices as defined
-in this Agreement (X marks for rights):
-
-+---------------------------------------------------------------+
-|                                 | Applications |    Devices   |
-+---------------------------------------------------------------+
-| ADP                             |      X       |              |
-+---------------------------------------------------------------+
-| ADE                             |      X       |              |
-+---------------------------------------------------------------+
-| DCP                             |      X       |      X       |
-+---------------------------------------------------------------+
-| DCE                             |      X       |      X       |
-+---------------------------------------------------------------+
-
-Licensed Software: Designer tools and modules
-
-The modules and/or tools that are included in the respective product - Qt for
-Design Studio Professional (DSP), Qt for Design Studio Enterprise (DSE) - are
-marked with "X" in the below table.
-
-Designer tools provides no Redistributables.
-
-+---------------------------------------------+
-|                                 | DSP | DSE |
-+---------------------------------------------+
-| Qt Design Studio                |  X  |  X  |
-+---------------------------------------------+
-| Qt Design Bridges               |     |  X  |
-+---------------------------------------------+
-| QML Live on host                |  X  |  X  |
-+---------------------------------------------+
-| QML Live on target              |     |  X  |
-+---------------------------------------------+
-| Variant Management              |     |  X  |
-+---------------------------------------------+
-| Shader creation tools           |     |  X  |
-+---------------------------------------------+
-| Profiling tools                 |     |  X  |
-+---------------------------------------------+
-| Simulink support                |     |  X  |
-+---------------------------------------------+
-
-
-Both DSP and DSE can be used to create an user interface for use cases covered
-by ADP, ADE, DCP and DCE.
-
-Licensed Software: QA Tools
-
-The modules and/or tools that are included in the respective QA Tools product
-- Squish (both Tester and execution Licenses), Coco or Test Center - are marked
-with "X" in the below table. Optional features that will need additional
-licenses are marked with "O". QA Tools include no Redistributables.
-
-+---------------------------------------------------------------------+
-|                                     | Squish |  Coco  | Test Center |
-+---------------------------------------------------------------------+
-| Squish IDE                          |    X   |        |             |
-+---------------------------------------------------------------------+
-| QA Tool-specific command line tools |    X   |   X    |      X      |
-+---------------------------------------------------------------------+
-| Coverage Browser                    |        |   X    |             |
-+---------------------------------------------------------------------+
-| HTML interface                      |        |        |      X      |
-+---------------------------------------------------------------------+
-| Qt Support Module                   |    X   |        |             |
-+---------------------------------------------------------------------+
-| Java support module                 |    X   |        |             |
-+---------------------------------------------------------------------+
-| Windows support module              |    X   |        |             |
-+---------------------------------------------------------------------+
-| iOS support module                  |    X   |        |             |
-+---------------------------------------------------------------------+
-| Android support module              |    X   |        |             |
-+---------------------------------------------------------------------+
-| Web support module                  |    X   |        |             |
-+---------------------------------------------------------------------+
-| macOS support module                |    X   |        |             |
-+---------------------------------------------------------------------+
-| VNC support module                  |    X   |        |             |
-+---------------------------------------------------------------------+
-| MCU support module                  |    X   |        |             |
-+---------------------------------------------------------------------+
-| C and C++ language module           |        |   X    |             |
-+---------------------------------------------------------------------+
-| C# language module                  |        |   X    |             |
-+---------------------------------------------------------------------+
-| QML language module                 |        |   X    |             |
-+---------------------------------------------------------------------+
-| Tester Cross-Compilation Add-On     |    O   |   O    |             |
-+---------------------------------------------------------------------+
-
-License capabilities for Squish
-
-License capabilities that are included in the Squish Tester and Execution
-Licenses are marked with "X" in the below table.
-
-+-----------------------------------------------------------------------------+
-|                          | Squish Tester License | Squish Execution License |
-+-----------------------------------------------------------------------------+
-| Ability to create, edit, |          X            |                          |
-| and debug test cas       |                       |                          |
-+-----------------------------------------------------------------------------+
-| Ability to execute test  |          X            |             X            |
-| cases                    |                       |                          |
-+-----------------------------------------------------------------------------+
-
-Install and use capabilities for QA Tools
-
-Install and use capabilities that are included in the respective QA Tools
-products are defined in the below table.
-
-+-----------------------------------------------------------------------------+
-|                         | Squish    | Squish     | Coco       | Test        |
-|                         | Tester    | Execution  | License    | Center      |
-|                         | License   | License    |            | License     |
-+-----------------------------------------------------------------------------+
-| Number of installation  | Unlimited | Unlimited  | Unlimited  | One(1)      |
-| instances per license   |           |            |            |             |
-+-----------------------------------------------------------------------------+
-| Number of concurrent    | Limited by| Limited by | Limited by | Limited by  |
-| users                   | number of | number of  | number of  | number of   |
-|                         | Squish    | Squish     | Coco       | Test Center |
-|                         | Tester    | Execution  | Tester     | Licenses    |
-|                         | Licenses  | Licenses   | Licenses   |             |
-+-----------------------------------------------------------------------------+
-
-
-APPENDIX 2: PRICING
-
-Separate template
-
-APPENDIX 3: ADD-ON PRODUCTS TO LICENSED SOFTWARE
-
-Intentionally left blank.
-
-APPENDIX 4: SMALL BUSINESS AND STARTUP
-
-The provisions of this Appendix 4 are applicable for companies with an annual
-revenue, including funding, equivalent to maximum of 250,000 USD (in applicable
-currency) during the latest full calendar year, as evidenced by duly audited
-records of the Licensee and approved by The Qt Company ("Start-up Company").
-
-Start-up Companies are qualified for a discounted License Fee for maximum of
-four (4) Development Licenses ("Start-up Development License") unless otherwise
-agreed between the parties.
-
-Start-up Development License entitles the respective Designated User for
-Support only for Install Support as defined in Appendix 9, Support Terms.
-
-Upon expiry of the respective Development License Term, the Start-up
-Development Licenses shall be automatically extended, pursuant to Section 3.1
-of the Agreement, for a Renewal Term either as new Start-up Development
-Licenses (if the Licensee still qualifies as a Start-up Company), or as normal
-then standard list price Development Licenses (if the Licensee no longer
-qualifies as a Start-up Company).
-
-APPENDIX 5: NON-COMMERCIAL AND EDUCATIONAL USE
-
-The provisions of this Appendix 5 are applicable for non-commercial use of the
-Licensed Software by the Licensee.
-
-For the purpose of this Appendix 5, the following additional definitions
-(replacing the relevant definition of the Agreement, where applicable) shall be
-applicable:
-
-"Demo Units" shall mean (i) hardware development platform, which incorporates
-the Licensed Software along with Licensee's software and/or hardware, and (ii)
-prototype versions of Applications or Devices.
-
-"Designated User(s)" shall mean the employees and students of the Licensee.
-
-"Licensee Products" shall mean Applications and/or Devices.
-
-"Permitted Purpose" shall mean (i) Licensee's internal evaluation and testing
-of Licensed Software, (ii) building Demo Units as well as (iii) educational
-use.
-
-"Agreement Term" shall mean a period of twelve (12) months or any such other
-period as may be agreed between the Parties.
-
-For the purpose of this Appendix 5, the following changes shall be agreed with
-respect to relevant Sections of the Agreement:
-    I. Recital (A) shall be replaced in its entirety to read as follows:
-           "(A) Licensee wishes to use the Licensed Software for the Permitted
-           Purpose."
-    II. Section 3.1 shall be replaced in its entirety to read as follows:
-        "The Qt Company grants to Licensee a personal, non-exclusive,
-        non-transferable, revocable, royalty-free license, valid for the
-        Agreement Term, to use, modify and copy the Licensed Software solely
-        for the Permitted Purpose. Licensee may install copies of the Licensed
-        Software on five (5) computers per Designated User, provided that only
-        the Designated Users who have a valid Development License may use the
-        Licensed Software. Licensee may demonstrate the Demo Units, provided
-        that such demonstrations must be conducted by Licensee, and the Demo
-        Units must remain in Licensee's possession and under Licensee's control
-        at all times.
-        For clarity, this Agreement does not (i) entitle Licensee to use
-        Licensed Software to create Applications or Devices (other than
-        prototypes thereof) or (ii) carry any distribution rights to Licensee,
-        but such rights are subject to and conditional upon conclusion of a
-        separate license agreement with The Qt Company."
-    III. Sections 3.2, 3.3, 3.5, 3.6, 8 and 10 shall be deleted.
-    IV. Section 3.4 shall be replaced in its entirety to read as follows:
-        "Licensee shall not:
-        - remove or alter any copyright, trademark or other proprietary rights
-          notice contained in any portion of the Licensed Software;
-        - transfer, publish, sublicense, disclose, display or otherwise make
-          the Licensed Software available to any third party (except that
-          Licensee may demonstrate the Demo Units pursuant to Section 3.1);
-        - in any way combine, incorporate or integrate Licensed Software with,
-          or use Licensed Software for creation of, any software created with
-          or incorporating Open Source Qt; Licensee shall cause all Designated
-          Users who make use of the licenses granted under this Agreement, to
-          be contractually bound to comply with the relevant terms of this
-          Agreement and not to use the Licensed Software beyond the terms
-          hereof. Licensee shall be responsible for any and all actions and
-          omissions of its Designated Users relating to the Licensed Software
-          and use thereof. Any use of Licensed Software beyond the provisions
-          of this Agreement is strictly prohibited and requires an additional
-          license from The Qt Company."
-    V. Section 12 shall be replaced in its entirety to read as follows:
-       "This Agreement shall enter into force upon due acceptance by both
-       Parties and remain in force for the Agreement Term, unless and until
-       terminated pursuant to the terms of Section 12.
-       Upon termination of the Agreement, Licensee shall cease using the
-       Licensed Software. All other copies of Licensed Software in the
-       possession or control of Licensee must be erased or destroyed. An
-       officer of Licensee must, upon request, promptly deliver to The Qt
-       Company a written confirmation that this has occurred."
-
-Except for the modifications specified above, this Appendix carries no change
-to the terms of the Agreement which shall remain in full force.
-
-APPENDIX 6: LICENSE REPORTING
-
-Separate template
-
-APPENDIX 7: MARKETING RIGHTS
-
-This Appendix 7 has the purpose to grant visibility through The Qt Company
-marketing channels of the usage of Qt and related product and service in
-Licensee product. Following related marketing right are agreed between the Qt
-Company and the Licensee.
-
-1. LICENSEE NAME AND LICENSEE LOGO
-
-The Qt Company has the right to use Licensee name and Licensee logo in public
-channel, in respect of the value proposition that the Qt company provided to
-the Licensee.
-
-2. MARKETING CONTENT COOPERATION
-
-2.1. LICENSEE CASES
-
-The Licensee is open to collaborate on content creation for marketing and
-communication purpose. The Licensee will nominate one responsible that will be
-in charge to support The Qt company with this content creation, according to
-content format paragraph, answering technical questions or sharing professional
-picture or video of required content. The Qt Company will have the right to
-advertise this in Content Format and Channel as mentioned in paragraph 3 and 4.
-
-2.2. FINAL PRODUCT REFERRAL
-
-Licensee agree that The Qt Company could connect their software product and
-services with the Licensee device or application, that the Licensee has created
-using The Qt Company technology and competence. Licensee will provide high
-quality picture, and video of the created final product where the Qt technology
-is running into. The Qt Company will have the right to advertise this in
-Content Format and Channel as mentioned in paragraph 3 and 4.
-
-3. CONTENT FORMAT
-
-- Video
-- Written Licensee case
-- Press release
-- Social media posts
-- Emails
-- Event booth Graphics
-- Printed material
-
-4. CHANNELS
-
-- Social media
-- The Qt Company resource center and website
-- Email to the Qt company contact database
-- Events
-- Online webinars
-- Public speech
-- Public presentations
-
-APPENDIX 8: INTENTIONALLY LEFT BLANK
-
-APPENDIX 9: SUPPORT TERMS
-Version 2023-04
-
-These Qt support terms and conditions (“Support Terms”) set forth the legal
-framework, where under The Qt Company (“The Qt Company”) provides support
-services (as herein defined) to the Licensee.
-
-1 DEFINITIONS
-
-“Application Code” shall mean a computer software program written strictly
-using the Qt programming language, by or for the Licensee, with a user
-interface, enabling the Licensee or their users to accomplish a specific task
-and display any results of the task on the display monitor or screen.
-
-“Customer Portal” shall mean The Qt Company’s web-based service and support
-user interface located at https://account.qt.io/ or at another location
-designated by The Qt Company. Customer Portal is used by a Designated User with
-Qt Account, and it provides downloads, license management, license certificate
-and other services for Designated Users.
-
-“Dedicated Contact” shall mean the employee of The Qt Company who will be the
-first point of contact for all Designated Users’ requests for Support.
-
-“Errors” shall mean an error, flaw, mistake, failure, or fault in Licensed
-Software that prevents it from behaving as described in the relevant
-documentation or as agreed between the Parties. Designated User can follow the
-state and progress of Errors in Customer Portal.
-
-“Extended Support” shall mean a continuation to the normal Support period,
-which allows Designated Users to receive selected Support (Standard Support or
-Premium Support) for a version of Licensed Software that is no longer generally
-supported by The Qt Company.
-
-”Install Support” shall mean Support that is limited to installation-related
-Error(s) on Development Platforms specified as supported host platforms for
-each Qt release under doc.qt.io. Install Support covers also operational use of
-the QA Tools, but not operational use of Qt Software.
-
-“Maintenance Release” shall mean a release or version of Licensed Software
-containing bug fixes, error corrections and other changes targeted to
-maintaining and improving product stability and quality. Maintenance Releases
-are generally depicted as a change to the third digit of Licensed Software
-version number.
-
-“Platforms” shall mean both Development Platforms and Deployment Platforms.
-Supported host and target Platforms may vary from for each Qt release as
-defined under doc.qt.io.
-
-“Premium Support” shall mean an upgraded level of Support that The Qt Company
-provides pursuant to these Support Terms to Licensee if Licensee has purchased
-Premium Support instead of Standard Support. Premium Support also covers what
-is included in Standard Support. Premium Support shall always be purchased for
-all Designated User(s) in the respective development team of the Licensee.
-
-“Qt Account” shall mean the Qt Account for a Designated User used for using Qt
-services and Customer Portal. A Qt Account is mapped to the Licensee company
-with the corporate email domain or domains.
-
-”Qualification Kit” shall mean a set of documents and validation test cases
-used for product certification needs as defined in section 2.6.
-
-“Response Time” shall mean the period of time from when Licensee notifies The
-Qt Company about an Error or requests Support until The Qt Company provides
-Licensee with a response that addresses (but not necessarily resolves) the
-reported Error or provides the requested Support.
-
-“Standard Support” shall mean standard level of Support that The Qt Company
-provides pursuant to these Support Terms to Licensee.  Standard Support also
-covers what is included in Install Support.
-
-“Security Issue” shall mean an Error that may cause a vulnerability in a system
-or application that uses the Licensed Software.
-
-“Support” shall mean developer assistance that is provided by The Qt Company to
-assist eligible Designated Users in Licensed Software installation, usage and
-functionality problem resolution for Error(s) and Error workarounds pursuant to
-the terms of these Support Terms. Support for different products is available
-as specified in the below table (‘X’ marking the Support that is included in
-the license price, optional Add-on Support services are marked as ‘O’):
-
-+-----------------------------------------------------------------------------+
-|                        | Install| Standard| Premium| Extended| Qualification|
-|                        | Support| Support | Support| Support | Kit          |
-+-----------------------------------------------------------------------------+
-| DSP                    |    X   |     X   |    O   |     O   |              |
-+-----------------------------------------------------------------------------+
-| DSE                    |    X   |     X   |    O   |     O   |              |
-+-----------------------------------------------------------------------------+
-| ADP                    |    X   |         |        |         |              |
-+-----------------------------------------------------------------------------+
-| ADE                    |    X   |     X   |    O   |     O   |              |
-+-----------------------------------------------------------------------------+
-| DCP                    |    X   |     X   |    O   |     O   |              |
-+-----------------------------------------------------------------------------+
-| DCE                    |    X   |     X   |    O   |     O   |              |
-+-----------------------------------------------------------------------------+
-| Squish                 |    X   |     X   |    O   |         |       O      |
-+-----------------------------------------------------------------------------+
-| Coco                   |    X   |     X   |    O   |         |       O      |
-+-----------------------------------------------------------------------------+
-| Test Center            |    X   |     X   |    O   |         |       O      |
-+-----------------------------------------------------------------------------+
-| Axivion Suite          |    X   |     X   |        |         |              |
-+-----------------------------------------------------------------------------+
-| Architecture Analysis  |    X   |     X   |        |         |              |
-+-----------------------------------------------------------------------------+
-| Static Code Analysis   |    X   |     X   |        |         |              |
-+-----------------------------------------------------------------------------+
-| Static Coverage        |    X   |     X   |        |         |              |
-| Analysis Professional  |        |         |        |         |              |
-+-----------------------------------------------------------------------------+
-| Qt Insight             |        |     X   |        |         |              |
-+-----------------------------------------------------------------------------+
-
-“Support Validity Term” shall mean the Development License Term or any other
-fixed time period agreed between the Parties during which time the Licensee is
-eligible to receive Support from The Qt Company.
-
-2 SUPPORT SERVICES
-
-2.1  Support Services Provided by The Qt Company
-
-Subject to these Support Terms and during the Support Validity Term, The Qt
-Company will via its Customer Portal, provide Designated User(s) with Support
-for the Licensed Software which Licensee has licensed under the Agreement. The
-Qt Company will make commercially reasonable efforts to solve any Errors
-reported by Designated User(s). Resolution of an Error may be provided through
-Designated User(s) themselves downloading of a later released version of the
-applicable Licensed Software product(s) or providing the Designated User with a
-workaround addressing such Error or providing the Designated User with an
-updated tool configuration.
-
-2.2  Licensee's Obligations
-
-To report an Error, the Designated User shall register the Error on the
-Customer Portal. If the Designated User considers the reported Error to be a
-Security Issue, the Error shall be marked as a Security Issue.
-
-The Designated User must provide adequate information and documentation to The
-Qt Company to enable it to recreate the Error or problem for which the
-Designated User has sought assistance. To ensure efficient handling of Errors,
-the Designated User must provide the following information, where relevant:
-- A clear, detailed description of the problem, question or suggestion;
-- Identification of which Licensed Software product and version is affected;
-- Identification of the operating environment (e.g. operating system, hardware
-  Platform, build tools, tool configuration, etc.) on which the problem exists;
-- Marking the issue as a Security Issue, when reporting a Security Issue;
-- On Standard Support: A complete and compilable test case of not more than 500
- lines of code that demonstrates the problem;
-- On Premium Support: A complete and compilable test case that demonstrates the
-  problem or access to Application Code source codes.
-
-Additional relevant content, such as screenshots, etc.
-Additional content should be included as attachments. The preferred image
-formats are JPEG and PNG.  Compressed content should be included in zip or
-tar.gz archives. Executable content and documents in platform specific formats
-such as Microsoft Office' are not accepted.
-
-In order for The Qt Company to provide prompt handling of Errors, the
-Designated User shall promptly respond to any requests from The Qt Company for
-additional information.
-
-2.3  Support Limitations
-
-General limitations:
-
-Each version or release of the Licensed Software will be Supported under
-Standard Support or Premium Support only for limited time period as set forth
-in doc.qt.io or in documentation provided with the respective Licensed Software
-product. If nothing is documented, a release of Licensed Software is supported
-for one (1) year from the release date of the version x.y.0 and Long Term
-Support (LTS) Releases are supported for a period of three (3) years from the
-release date of the LTS version x.y.0.
-
-The Qt Company shall only provide Support for Designated User(s) through
-Customer Portal.Support is made available for the entire development teams
-only: It is not allowed to purchase Support only for some members of the
-development team, and all Designated Users of the respective development team
-must be eligible for the same level of Support.
-
-Support is not provided for snapshots, preview releases, beta releases or
-release candidates.
-
-The Qt Company shall have no obligation to provide Support for 3rd party
-components, hardware or operating system specific problems or problems arising
-from improper use, accident, neglect, or modification of Qt.
-
-Limitations with Install Support:
-
-Support limited to (i) Error(s) regarding installation and setting up of the Qt
-development environment on host Platforms, or (ii) Errors impacting operational
-use of the QA Tools.
-
-Limitations with Standard Support:
-
-The Qt Company shall not provide Support for third-party software or problems
-caused by third-party software even if such third-party software is distributed
-together with Licensed Software product(s).
-
-The Qt Company shall only provide Support for Error(s) that are reported on and
-can be reproduced on Platforms that are officially supported for the release of
-the Licensed Software.
-
-Limitations with Premium support:
-
-The Qt Company shall not provide Support for third-party software or problems
-caused by third-party software. However, if such third-party software is
-distributed together with Licensed Software, The Qt Company will make
-commercially reasonable efforts to solve such problems.
-
-The Qt Company shall only provide Support for Error(s) that can be reproduced
-on Platforms that are officially supported for the release of the Licensed
-Software. If the Error is on a Platform that is not supported, The Qt Company
-will make commercially reasonable efforts to provide a solution on closest
-corresponding supported Platform.
-
-Premium Support is optional and purchased for an agreed bucket of hours
-(“Bucket”). Hours can be used by any Designated User in the respective
-development team. To encourage continuous usage of the Support, ten percent
-(10%) of the purchased Bucket shall automatically expire (regardless of whether
-such support hours are actually used or not by the Licensee) each month after
-three (3) months from the purchase of the Premium Support.
-
-2.4 Handling of Security Issues
-
-The reported Errors marked as Security Issues will be assessed by experts to
-determine the severity of the issue and to verify if it indeed is a valid
-Security Issue. The Designated User who reported the issue may be contacted for
-more details. If the reported issue is not deemed to be a Security Issue, it
-will be treated as a normal Error and handled accordingly.
-
-A verified Security Issue will be fixed as soon as possible. Qt Company will
-notify all Licensees via appropriate channels about the Security Errors and
-availability of patches for Licensed Software. Typically, a fix for the
-Security Issue is included in the next Maintenance Release of Licensed
-Software.
-
-If the Security Issue is reported in a third-party library used in Licensed
-Software, The Qt Company will notify the relevant third party of such Security
-Issue detected in their library. When the Security Issue is fixed in the
-third-party library, the new version of the third-party library will be in the
-next feasible Maintenance Release of the Licensed Software. If a fixed version
-of the third-party library is not available, The Qt Company may instead decide
-to include documentation regarding the issue, or a patch for this third-party
-library.
-
-All known Security Issues in Licensed Software will be mentioned as part of the
-change notes released with each version of Licensed Software.
-
-2.5 Extended Support
-
-Extended Support extends the Support Validity Term for a release of Licensed
-Software that is no longer generally supported.
-
-Extended Support includes and is by default provided with Standard Support
-rules and limitations, unless Extended Support is purchased together with
-Premium Support in which case Premium Support rules and limitations will apply.
-
-Extended Support is optional and purchased with annual fee and separately per
-each Licensee product. Extended Support will need definition of (i) Licensee
-product, (ii) used Platform(s) and (iii) Licensed Software version(s). For
-avoidance of doubt, Extended Support requires that the Designated User has a
-valid license for the respective Licensed Software.
-
-2.6 Qualification Kit
-
-The Qt Company shall provide a set of documents and validation tests that
-enable the Licensee to qualify QA testing tool (subject to a separate fee) or
-Qt Safe Renderer for the purpose of safety certification of Licensee end-to-end
-solution. Exact complied safety standards may vary between products, used
-features, use case, and industry.
-
-3 RESPONSE TIME
-
-In performing Support, The Qt Company shall commit to following, non-binding,
-Response Times:
-
-Standard Support: Errors and Support requests will have a Response Time not to
-exceed two (2) business days.
-
-Premium Support: Errors and Support requests will have a Response Time not to
-exceed one (1) business day.
-
-Security Issues: Errors that are Security Issues will have a Response Time not
-to exceed one (1) business day.
-
-For complex issues, The Qt Company may provide an initial response to the
-Designated User and then follow up, without undue delay, with additional
-communication before an Error is properly addressed or Support provided.
-
-4 ADDITIONAL SERVICES IN PREMIUM SUPPORT
-
-The Designated User(s) will be assigned a Dedicated Contact to handle requests
-for Support. Dedicated Contact is subject to change in cases such as sick
-leave, vacation and other similar reasons.
-
-The Designated User(s) can on request ask The Qt Company to access their
-computer remotely in order to resolve problems directly.
-
-The Designated User(s) can request a session via Instant Messaging or phone
-call in the support request to The Qt Company.
-
-Premium Support can assist Licensee in implementing new features, bug fixes
-and accessing patches in Licensed Software or Application Code.
-
-All Support requests will be handled with high priority.
-
-5 MAINTENANCE RELEASES, UPDATES AND UPGRADES
-
-Under the Support the Licensee is eligible for Maintenance Releases and Updates
-that The Qt Company generally makes available to customers who has purchased
-Support. Unless otherwise decided by The Company at its free and absolute
-discretion, Upgrades will not be provided under the Support.
-
-The primary focus of Maintenance Releases is product quality.  Therefore, each
-Maintenance Release typically includes the following types of changes to the
-previous version of Licensed Software:
-- Bug fixes caused by changes to previously working code;
-- Fixes related to build issues on supported Platforms;
-- Error corrections specific to a single Platform that are not present on other
-  Platforms;
-- Corrections to Security Issues;
-- Critical Error corrections such as crashes, data corruption, loss of data,
-  race conditions; and
-- Updates to documentation and license information when deemed necessary by
-  The Qt Company.
-
-The primary focus of Updates is introducing new features to Licensed Software
-and covering new platforms. Therefore, each Updates typically includes the
-following types of changes to the previous version of Licensed Software:
-- New platform support;
-- New toolchain support;
-- New features and Qt modules;
-
-6 WARRANTY DISCLAIMER
-
-The Qt Company makes no warranties that the Support provided will be successful
-in resolving any difficulties or problems or in diagnosing faults reported by
-Licensee. Support is provided to Licensee on an "as is" basis.  To the maximum
-extent permitted by applicable law, The Qt Company disclaims all warranties and
-conditions, either express or implied, including, but not limited to, implied
-warranties of merchantability and fitness for a particular purpose for the
-Support provided by The Qt Company to Licensee.
-
-APPENDIX 10: CONVERSION TO SUBSCRIPTION
-
-Subject to the terms of this Appendix Licensee's current development licenses
-("Current Licenses") for commercial version of Qt Software and the license
-agreements governing such Current Licenses ("Existing Agreements") are being
-replaced by this Agreement and subscription based Development Licenses
-governed hereunder, as further specified below.
-
-+---------------------------------------------------------------------------+
-| Existing Agreement(s)    | <Trolltech, Nokia, Digia, The Qt Company> and  |
-| signing parties, version | <Licensee> <Version of the Agreement, e.g. 2,0,|
-| and date of signatures   | 3.2 or 4.1> <Date of the agreement signatures> |
-| thereof                  |                                                |
-+---------------------------------------------------------------------------+
-
-Parties hereby agree on conversion of Current Licenses listed in attached
-Exhibit A to the subscription licenses listed in attached Exhibit B for use
-through License Term. As of the date hereof,
-
-i. Licensee's Current Licenses as listed in Exhibit A shall terminate and be
-replaced with the Subscription licenses listed in Exhibit B and;
-ii. Existing Agreements are terminated.
-
-Prices for the conversion of Current Licenses are defined in Appendix 2
-Pricing or Quote.
-
-Notwithstanding anything in this Appendix to the contrary, and in addition to
-any payments due pursuant to this Appendix, Licensee remains fully obligated to
-fulfill any and all outstanding payment obligations to The Qt Company under any
-applicable Existing Agreements. For the avoidance of doubt, if any payments
-remain outstanding on the Current Licenses under the applicable terms Licensee
-will continue to make such payments in accordance with the applicable order
-documentation, notwithstanding the fact that the Current Licenses are being
-converted to Development Licenses pursuant to this Appendix.
-
-APPENDIX 11: TERMS OF USE - QT INSIGHT TRACKER LIBRARY
-Version 1.0
-
-Qt Insight Tracker Library ("Tracker Library") is a software module used to
-collect end user data from Customer's Application and Devices relating to The
-Qt Company's Qt Insight online service the Customer is ordering from The Qt
-Company under a separate service agreement ("Service Agreement").
-
-Unless otherwise set forth herein, definitions written in capital letters used
-herein shall have the meaning set forth in the Service Agreement.
-
-Subject to these terms The Qt Company grants to Customer a worldwide,
-non-exclusive, non-transferable, royalty-free, revocable (for cause) right and
-license, valid for the term of the Service Agreement, to
-    (i)  use, copy and modify Tracker Library for the purpose of including it
-         into the Devices and Applications and solely for the purpose of being
-         used only in conjunction with Insight Cloud or Insight Private Cloud,
-         and
-    (ii) distribute, by itself or through its Contractors, Tracker Library as
-         installed, incorporated, or integrated into Applications and/or
-         Devices.
-
-Use of Tracker Library in a way or for the purpose other than the above is
-strictly prohibited. Tracker Library is licensed to the Customer in all
-respects "as is".
-
-TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF
-ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER
-WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
-NON-INFRINGEMENT WITH REGARD TO THE TRACKER LIBRARY. THE QT COMPANY DOES NOT
-WARRANT THAT THE TRACKER LIBRARY WILL SATISFY CUSTOMER'S REQUIREMENTS OR THAT
-IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE
-UNINTERRUPTED.
-IN NO EVENT SHALL THE QT COMPANY BE LIABLE TO THE CUSTOMER FOR ANY LOSS OF
-PROFIT, LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT,
-SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY
-KIND, HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THE USE OF THE TRACKER
-LIBRARY.
-
-THE TOTAL MAXIMUM LIABILITY OF THE QT COMPANY UNDER THESE TERMS SHALL IN NO
-EVENT EXCEED 10,000 EUROS.
+Qt Frame Agreement
+Version 2024-02
+
+1. PARTIES OF THIS AGREEMENT
+1.1. This Qt Frame Agreement—comprised of these general terms together with the appendices attached hereto, (hereinafter “Agreement”) is made by and between: The Qt Company, as defined below (hereinafter ”The Qt Company”) AND Customer name (hereinafter “Customer"):___________________ Business Id (e.g. VAT or EIN number):___________________
+1.2. The parties above are hereinafter individually referred to as a "Party" and collectively as the "Parties".
+
+2. STRUCTURE AND OBJECT OF THE AGREEMENT
+2.1. The Parties have entered into this Agreement to agree on the terms and conditions applicable to The Qt Company's delivery of products and services ("Services") to Customer.
+2.2.  This Agreement is comprised of the following components:
+(i) This Agreement, which contains the general terms applicable to all Services,
+(ii) Appendices for each of the Services, containing terms applicable to that individual set of Services ("Service Terms"),
+(iii) a Qt Appendix for Pricing, if applicable, which contains pricing for specific Services, and
+(iv) other topic-specific appendices, such as Support or Marketing Rights.
+2.3. Any and all Services purchased shall be specified in, and agreed upon between, the Parties under a separate purchase order, statement of work, quote, or similar document ("Purchase Document"). Each Purchase Document concluded under this Agreement shall include a reference to this Agreement and be governed by this Agreement.
+
+3. DEFINITIONS
+3.1. "Affiliate" of a Party shall mean an entity (i) which is directly or indirectly controlling such Party; (ii) which is under the same direct or indirect ownership or control as such Party; or (iii) which is directly or indirectly owned or controlled by such Party. For these purposes, an entity shall be treated as being controlled by another if that other entity has fifty percent (50 %) or more of the votes in such entity, is able to direct its affairs and/or to control the composition of its board of directors or equivalent body.
+3.2. "Contractor" shall mean third-party consultants, distributors and contractors performing services to Customer under an applicable contractual arrangement.
+3.3. "Customer" shall mean the individual or legal entity specified in Section 1 above, that is a Party to this Agreement.
+3.4. "Force Majeure Event" shall have the meaning set forth in Section 11.7.
+3.5. "Licensed Software" shall mean The Qt Company's commercial software product which is licensed for use by Customer under this Agreement and corresponding Service Terms. Licensed Software shall include, if and to the extent applicable and specified in the applicable relevant Service Terms, corresponding online or electronic documentation, associated media and printed materials, including the source code, and example programs. The Qt Company may in the course of its development activities, at its free and absolute discretion and without any obligation to send or publish any notifications to  Customer or in general, make changes, additions or deletions in the components and functionalities of the Licensed Software, provided that no such changes, additions or deletions will affect the already released version of the Licensed Software, but only upcoming version(s). Licensed Software is commercial computer software, developed at private expense and offered to the public under standard commercial terms.
+3.6. "Professional Services" shall mean The Qt Company's professional-, consulting-, training- and/or project services delivered to Customer under this Agreement and specified in a Purchase Document.
+3.7. "Support" shall mean maintenance and support services provided by The Qt Company to assist Customer in using the Licensed Software, as further specified in the Appendix for Support Terms.
+3.8. "The Qt Company" shall mean:
+(i) in the event Customer is an entity residing in the United States or a legal entity incorporated in or having its headquarters in the United States, The Qt Company Inc., a Delaware corporation with its office at 3031 Tisch Way, 110 Plaza West, San Jose, CA 95128, USA.; or
+(ii) in the event Customer is an entity residing outside of the United States or a legal entity incorporated or having its registered office outside of the United States, The Qt Company Oy., a Finnish company with its registered office at Miestentie 7, 02150 Espoo, Finland.
+
+4. PRICES AND PAYMENT
+4.1. The Qt Company agrees to make Services available to Customer subject to the prices set forth in the Appendix for Pricing. In the event that the Appendix for Pricing does not include a price for certain Services, the applicable price shall be the price agreed by the Parties in the respective Purchase Document.
+4.2. All prices are exclusive of value added tax or other taxes, levels, or duties. Value added tax as well as other possible public charges imposed by authorities shall be added to the prices.
+4.3. All fees under this Agreement are non-cancellable and non-refundable.
+4.4. All fees under this Agreement shall be paid by Customer no later than thirty (30) days from the date of the applicable invoice from The Qt Company.
+4.5. Unless otherwise agreed or provided in the respective Service Terms or Purchase Document, The Qt Company will invoice fees for:
+4.5.1. Licensed Software and Support in advance upon conclusion of the Purchase Document, and
+4.5.2. Professional Services monthly in arrears after the Service has been performed.
+4.6. A late payment charge of the lower of: (a) one percent (1%) per month; or (b) the highest interest rate stipulated by applicable law, shall be charged on any unpaid balances that remain past due and which have not been disputed by Customer in good faith within thirty (30) days of receipt of invoice from The Qt Company.
+4.7. The Qt Company may either (i) invoice Customer based on existing agreement, (ii) request Customer to place a purchase order corresponding to a quote by The Qt Company, or (iii) use Customer's stored Credit Card information to automatically charge the Customer for the relevant Renewal Term.
+4.8. Unless and to the extent otherwise agreed in the Appendix for Pricing or in the Purchase Document, The Qt Company shall be entitled to adjust the prices set forth in the Appendix for Pricing by notifying  Customer of the change in writing at least sixty (60) days before the effective date of the change. The change shall not affect the current pricing term of Services agreed upon before the effective date of the change.
+
+5. CONFIDENTIALITY
+5.1. The Parties shall keep confidential, and shall not use or disclose to any unauthorized third parties, the existence and content of this Agreement as well as any Confidential Information received from the other Party or otherwise learned in connection with the Agreement or the performance of the Services, without the prior written consent of the other Party. Confidential Information shall mean information that is designated as confidential or that would be reasonably understood to be confidential given the circumstances of disclosure and the nature of the information. The Parties shall not use Confidential Information received from the other Party for any other purposes than the performance of the Agreement or the fulfilment of their rights and obligations hereunder.
+5.2. Each Party shall limit access the other Party's Confidential Information only to those of its employees, subcontractors, Contractors, Affiliates or financial or legal advisors who necessarily need access to the Confidential Information for the proper performance of the Party's rights and obligations under the Agreement. Each Party shall ensure that the persons receiving Confidential Information of the other Party are bound by confidentiality obligations not less restrictive than those stipulated herein.
+5.3. Each Party shall protect the confidentiality of the other Party's Confidential Information with at least  the same degree of security as it exercises to its own confidential information, but no less than a standard of reasonable care.
+5.4. The confidentiality obligation stipulated herein shall not be applied to material and information which:
+(iii) has become generally available or otherwise public prior to its submission by the other Party;
+(iv) becomes generally available or otherwise public due to a reason other than the negligence or omission of the recipient or its personnel or other actions in violation of this Agreement or  applicable legislation;
+(v) the Party has lawfully received from a third party without any obligation of confidentiality;
+(vi) was lawfully in the possession of the receiving Party prior to receipt of the same from the other Party without any obligation of confidentiality related thereto;
+(vii) a Party has developed independently without using material or information received from the other Party; or
+(viii) a Party must disclose pursuant to law, decree or other order issued by competent regulatory or governmental body or other public authority or a judicial order, in which case the Party shall, to the extent permitted by applicable law, inform the other Party in writing of the disclosure of information prior to such disclosure.
+5.5. Each Party shall, upon request of the other Party at any time, including upon termination, cancellation or expiry of the Agreement, promptly destroy or deliver to the other Party any and all the documents, files, copies and material containing Confidential Information of the other Party. Notwithstanding the foregoing, a Party may retain one copy of the Confidential Information in a secure location, if and solely to the extent required to comply with applicable laws or regulations. Any Confidential Information stored in electronic back-up form shall be rendered inaccessible and destroyed in accordance with standard back-up procedures.
+
+6. INTELLECTUAL PROPERTY RIGHTS
+6.1. Unless and to the extent expressly provided in the respective Service Terms, this Agreement carries no assignment or license to the intellectual property rights of either Party and all such rights are and shall remain the exclusive property of the Party to whom such rights are vested under applicable law at the signing of this Agreement or thereafter.
+6.2. Where The Qt Company's delivery includes any materials owned by a third party, such third party materials shall be governed in all respects by the applicable license terms of such third-party right holders. The Qt Company shall duly inform the Customer whenever such third party materials are included in the Services and of applicable license terms to be followed by the Customer in using such third party materials.
+
+7. FEES AND ORDERING
+7.1. Services Fees. Services Fees are described in the Purchase Document.
+7.2. Ordering Services.
+(i) Customer may purchase Services pursuant to agreed pricing terms or, if no specific pricing terms have been agreed upon, at The Qt Company's standard pricing terms applicable at the time of purchase.
+(ii) Unless expressly otherwise agreed, any price or other term quoted to Customer shall only be valid for the thirty (30) days from the date such price has been quoted.
+
+8. LIMITED WARRANTY AND WARRANTY DISCLAIMER
+8.1. The Qt Company hereby represents and warrants that: (i) it has the power and authority to grant the rights and licenses granted to Customer under this Agreement; (ii) the Licensed Software will operate materially in accordance with its specifications (as set forth in the applicable product documentation or, where relevant, program description); (iii) Professional Services and Support will be performed in a professional, workmanlike manner pursuant to the Agreement; and (iv) during the ten years prior to the effective date of this Agreement, there have not been any claims alleging that the Licensed Software has infringed any intellectual property rights of a third party and, to the knowledge of The Qt Company as of the effective date of this Agreement, no such infringement exists. These warranties do not apply to issues arising from, or relating to, any third-party materials or Customer's use of the Licensed Software in violation of applicable law or the terms of this Agreement.
+8.2. Except to the extent set forth above, the Services are delivered to Customer "as is" and to the maximum extent permitted by applicable law, exclusive of other warranties, whether express, implied, or otherwise.  Customer's sole and exclusive remedy and The Qt Company's entire liability for deficiencies or errors in the Services shall be limited, at The Qt Company's option, to correction of the error, replacement of the Services, re-performance of the Service  or return of the applicable fees paid for the defective Service for the time period during which Customer was not able to utilize the Service as agreed.
+
+9. LIMITATION OF LIABILITY
+9.1. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, (II) A BREACH OR VIOLATION OF THE OTHER PARTY'S INTELLECTUAL PROPERTY RIGHTS, OR (III) WHERE REQUIRED BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOST PROFITS, LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT.
+9.2. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, (II) A BREACH OR VIOLATION OF THE OTHER PARTY'S INTELLECTUAL PROPERTY RIGHTS, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT EXCEED THE AGGREGATE FEES PAID OR PAYABLE TO THE QT COMPANY BY CUSTOMER FOR THE RESPECTIVE LICENSED SOFTWARE OR SERVICE GIVING RISE TO THE LIABILITY. THE FOREGOING LIMITATION WILL NOT APPLY TO CUSTOMER'S OBLIGATION TO PAY THE APPLICABLE FEES CORRESPONDING TO ITS  ACTUAL USE OF LICENSED SOFTWARE OR SERVICES.
+
+10. TERM AND TERMINATION
+10.1. This Agreement shall enter into force upon signing by both Parties and is effective as of the last date of signature.
+10.2. This Agreement shall remain in force until further notice and may be terminated without cause by either Party by no less than three (3) months' prior written notice to the other Party.
+10.3. Termination of a particular Purchase Document and the Services governed thereunder shall be stipulated under the applicable Service Terms.
+10.4. Either Party may terminate this Agreement with immediate effect, if the other Party:
+(i) commits a material breach of the terms of this Agreement (including applicable Service Terms) and has not remedied such breach within a reasonable period of time (which shall be no less than thirty (30) days) of the non-breaching  Party's written notice specifying the breach, or
+(ii) becomes bankrupt, insolvent or goes into liquidation or debt restructuring.
+10.5. Termination of this Agreement shall, as such, have no effect on the validity of any Services ordered and agreed prior to the effective date of such termination, and such Services shall continue to remain in force pursuant to applicable Service Terms (including the terms of this Agreement) for the remainder of the duration of the applicable Service validity term.
+
+11. GOVERNING LAW AND DISPUTE RESOLUTION
+11.1. The United Nations Convention on Contracts for the International Sale of Goods will not apply to this Agreement.
+11.2. Where this Agreement is concluded with The Qt Company, Inc., a Delaware corporation, the Parties agree that this Section 10.2 will apply. This Agreement will be governed by, and construed in accordance with the laws of the State of California and any controlling United States federal law. Any dispute, controversy or claim arising out of or relating to this contract, including the formation, interpretation, breach or termination thereof, and whether the claims asserted are arbitrable, will be referred to and finally determined by arbitration in accordance with the JAMS International Arbitration Rules. The tribunal will consist of one arbitrator. The place of arbitration will be San Francisco, California, USA. The language to be used in the arbitral proceedings will be English. Judgment upon the award rendered by the arbitrator(s) may be entered in any court having jurisdiction thereof. This Section 10.2 shall not preclude parties from seeking provisional remedies in aid of arbitration from a court of appropriate jurisdiction. Notwithstanding the foregoing, any action by The Qt Company solely to collect license or other fees hereunder may be brought in any court of competent jurisdiction.
+11.3. Where this Agreement is concluded with The Qt Company, Oy., a Finnish company, the parties agree that this Section 10.3 will apply. This Agreement shall be construed and interpreted in accordance with the laws of Finland, excluding its choice of law provisions. All disputes arising out of or in connection with this Agreement shall be finally settled in accordance with the laws of Finland, excluding its choice of law provisions.  All disputes arising out of or in connection with this Agreement shall be finally settled under the Rules of Arbitration of the International Chamber of Commerce by one or more arbitrators appointed in accordance with the said Rules. The place of arbitration will be Helsinki, Finland. The language to be used in arbitral proceedings will be English. This Section 10.3 shall not preclude parties from seeking provisional remedies in aid of arbitration from a court of appropriate jurisdiction.
+
+12. MISCELLANOUS
+12.1. No Assignment. Customer shall not be entitled to assign or transfer all or any of its rights, benefits and obligations under this Agreement except in case of sale of relevant business or assets or otherwise with prior written consent of The Qt Company, which shall not be unreasonably withheld or delayed. The Qt Company shall be entitled to freely assign or transfer any of its rights, benefits or obligations under this Agreement.
+12.2. Surviving Sections. Any terms and conditions that by their nature or otherwise reasonably should survive termination of this Agreement shall so be deemed to survive.
+12.3. Entire Agreement. This Agreement, its Appendices and any applicable Purchase Documents constitute the complete agreement between the Parties and supersedes all prior or contemporaneous discussions, representations, contracts (including prior License Agreements and similar prior agreements), and proposals, written or oral, with respect to the subject matters discussed herein.
+12.4. Subcontractors. The Qt Company may utilize subcontractors in the performance of Services under this Agreement, provided that The Qt Company remains responsible for the performance of the Services and compliance with this Agreement, as well as ensuring that subcontractors are required to abide by relevant restrictions (e.g., confidentiality) set forth in this Agreement.
+12.5. Modifications. No modification of this Agreement shall be effective unless contained in a writing executed by an authorized representative of each Party. No standard terms and conditions or provisions of any Customer purchase order or other ordering form that Customer may use in connection with the acquisition of Services will modify or affect this Agreement, the parties agree that any such terms and conditions are void with no legal effect.
+12.6. Affiliate Orders. Customer Affiliates may purchase Services via this Agreement as follows:
+(i) any purchases by Customer Affiliates from The Qt Company or its Affiliates will create a contractual relationship directly between the relevant The Qt Company entity and the respective ordering Customer Affiliate;
+(ii) the entry into a Purchase Document between The Qt Company and Customer Affiliate creates an agreement between The Qt Company and Customer Affiliate and incorporates all terms and conditions of this Agreement as the governing agreement between The Qt Company and Customer Affiliate ("Accession Agreement"): and
+(iii) Customer Affiliate will be deemed "Customer" under the terms of this Agreement and all rights and obligations under such Accession Agreement are vested and borne solely by the ordering Customer Affiliate and the relevant The Qt Company entity as contracting parties under such Accession Agreement.
+12.7. Force Majeure. Neither Party shall be liable to the other for any delay or non-performance of its obligations hereunder in the event and to the extent that such delay or non-performance is due to an event of act of God, terrorist attack or other similar unforeseeable catastrophic event that prevents either Party for fulfilling its obligations under this Agreement and which such Party cannot avoid or circumvent ("Force Majeure Event"). If the Force Majeure Event results in a delay or non-performance of a Party for a period of three (3) months or longer, then either Party shall have the right to terminate the relevant Purchase Document and Services thereunder with immediate effect without any liability (except for the obligations of payment arising prior to the Force Majeure Event) towards the other Party.
+12.8. Notices. Any notice given by one Party to the other shall be deemed properly given and deemed received if specifically acknowledged by the receiving Party in writing or when successfully delivered to the recipient by hand, fax, or special courier during normal business hours on a business day to the addresses specified for each Party in this Agreement. Each communication and document made or delivered by one Party to the other Party pursuant to this Agreement shall be in the English language.
+12.9. Attorney Fees. The prevailing Party in any action to enforce this Agreement shall be entitled to recover its attorney's fees and costs in connection with such action.
+12.10. Privacy and Security. The Parties commit to and comply with their respective applicable obligations under the privacy and security terms set forth in the Privacy and Security Appendix and relevant Appendices attached hereto.
+12.11. Feedback. Customer agrees that, from time to time, The Qt Company, may request feedback from Customer regarding the Services ("Feedback"). Customer may choose to provide Feedback and agrees that The Qt Company may freely use, copy, disclose, and exploit any Feedback. No Feedback will be considered Customer Confidential Information unless explicitly agreed otherwise between the Parties.
+12.12. Export Control. Customer acknowledges that the Services, or portions thereof, may be subject to export control restrictions under the applicable laws of respective countries. Customer shall fully comply with all applicable export license restrictions and requirements, economic sanctions restrictions, as well as with all laws and regulations relating thereto, and shall procure all necessary governmental authorizations, including without limitation, all necessary licenses, approvals, permissions, or consents, where necessary (e.g., for re-exportation of the Redistributables, Applications and/or Devices, each as defined in the relevant Service Terms).
+12.13. Severability. If any provision of this Agreement shall be adjudged by any court of competent jurisdiction to be unenforceable or invalid, that provision shall be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable.
+
+13. APPENDICES
+13.1. The following appendices form an integral part of this Agreement. In case of a discrepancy between this Agreement and any of its Appendices, this Agreement shall prevail. In case of discrepancies between the Purchase Document(s) and this Agreement or applicable Service Terms, the terms of this Agreement or the applicable Service Terms shall prevail, except in cases where an express deliberate deviation from the terms of this Agreement or applicable Service Terms has been concluded pursuant to Section 2.3 hereof, in which case the Purchase Document shall prevail.
+1. Appendix for Qt Development Framework
+2. Appendix for Support Terms https://www.qt.io/terms-conditions/support-terms
+3. Appendix for Privacy and Security Terms https://www.qt.io/terms-conditions/privacy-and-security
+
+Appendix for Qt Development Framework
+Version 2024-02
+
+1. This Appendix for Qt Development Framework is an integral part of the Agreement and specifies the legal terms for the licensing of Licensed Software (as defined below) between The Qt Company and the Customer. Entry into this Appendix governs the use of and supersedes any prior contracts between the Parties (including prior License Agreements and similar prior agreements), with respect to the Licensed Software under this Appendix.
+
+2. DEFINITIONS
+2.1. Capitalized words used in this Appendix shall have the meanings described in the Agreement or as defined below.
+2.2. "Add-on Products" shall mean The Qt Company's specific add-on software products which are not licensed as part of The Qt Company's standard Services offerings, but shall be included into the scope of Licensed Software only if so specifically agreed between the Parties.
+2.3. "Application" means software products created using the Licensed Software, which include the Redistributables, or part thereof.
+2.4. "End Customer" shall mean Customer's customer(s) to whom Customer, directly or indirectly, distributes copies of the Redistributables as integrated or incorporated into Applications or Devices.
+2.5. "Data Protection Legislation" shall mean the General Data Protection Regulation (EU 2016/679) (GDPR) and any national implementing laws, regulations and secondary legislation, as may be amended or updated from time to time, as well as any other data protection laws or regulations applicable in the relevant territory.
+2.6. "Deployment Platforms" shall mean target operating systems and/or hardware specified in the License Certificate, on which the Redistributables can be distributed pursuant to the terms and conditions of this Appendix.
+2.7. "Designated User(s)" shall mean the employee(s) of Customer or Customer's Affiliates acting within the scope of their employment or Customer's Contractors acting within the scope of their services on behalf of Customer.
+2.8. "Development License" shall mean the license needed by the Customer for each Designated User to use Licensed Software under the license grant described in Section 5 of this Appendix. Development Licenses are available per respective Licensed Software products; each product having its designated scope and purpose of use.
+2.9. "Development Platforms" shall mean the host operating system(s) specified in the License Certificate, on which Licensed Software can be used under the Development License.
+2.10. "Devices" shall mean
+(i) hardware devices or products that
+a. are manufactured and/or distributed by the Customer, its Affiliates, Contractors or End Customer, and
+b. incorporate, integrate or link to Applications such that substantial functionality of such unit, when used by an End User, is provided by Application(s) or otherwise depends on the Licensed Software; or
+(ii) Applications designed for the hardware devices specified in item (i).
+Devices covered by this Appendix shall be specified in the Pricing Appendix or Purchase Document.
+2.11. "Distribution License(s)" shall mean a royalty-bearing license required for any kind of sale, trade, exchange, loan, lease, rental or other distribution by or on behalf of Customer to a third party of Redistributables in connection with Devices pursuant to license grant described in Section 5.3 of this Appendix. Distribution Licenses are sold separately for each type of Device respectively and cannot be used for any other type of Devices.
+2.12. "Distribution License Packs" shall mean set of prepaid Distribution Licenses for distribution of Redistributables, as defined in The Qt Company's standard price list, quote, Pricing Appendix  or in the Purchase Document, as applicable.
+2.13. "Evaluation License Term" shall mean a time period specified in the License Certificate for the Customer to use the relevant Licensed Software for evaluation purposes according to Section 5.6 of this Appendix.
+2.14. "Intellectual Property Rights" shall mean patents (including utility models), design patents, and designs (whether or not capable of registration), chip topography rights and other like protection, copyrights, trademarks, service marks, trade names, logos or other words or symbols and any other form of statutory protection of any kind and applications for any of the foregoing as well as any trade secrets.
+2.15. "License Certificate" shall mean a certificate generated by The Qt Company for each Designated User respectively upon their download of the Licensed Software, which will be available under the respective Designated User's Qt Account at account.qt.io. License Certificates will specify relevant information pertaining to the Licensed Software purchased by Customer and the license to the Licensed Software.
+2.16. "License Fee" shall mean the fee charged to Customer for rights granted under this Appendix.
+2.17. "Licensed Software" shall mean the specified product(s) of Qt Software which Customer has purchased and which is provided to Customer under the terms of this Appendix (including its Exhibits). Licensed Software shall include corresponding online or electronic documentation, associated media and printed materials, including source code (where applicable), example programs and the documentation. Licensed Software does not include Third Party Software (as defined in Section 6) or Qt Community Edition. The Qt Company may, in the course of its development activities, at its free and absolute discretion and without any obligation to send or publish any notifications to Customer or in general, make changes, additions or deletions in the components and functionalities of the Licensed Software, provided that no such changes, additions or deletions will affect the already released version of the Licensed Software, but only upcoming version(s).
+2.18. "License Term" shall mean the agreed validity period of the Development License during which the relevant Licensed Software product can be used pursuant to this Appendix. The agreed License Term, as ordered and paid for by Customer, shall be memorialized in the applicable License Certificate.
+2.19. "Customer's Records" shall mean books and records that contain information bearing on Customer's compliance with the  Agreement, Customer's use of Qt Community Edition and/or the payments due to The Qt Company under the Agreement, including, but not limited to user information, assembly logs, sales records and distribution records.
+2.20. "Modified Software" shall have the meaning as set forth below in Section 4.
+2.21. "Qt Software" shall mean the development and design software of The Qt Company, which The Qt Company makes available under commercial and/or open source licenses as either the "Licensed Software" or the "Qt Community Edition".
+2.22. "Permitted Software" shall mean third party products that are generally available to the public, which may include parts of Qt Community Edition or be developed using Qt Community Edition.
+2.23. "Pre-Release Code" shall have the meaning as set forth in Section 7.
+2.24. "Prohibited Combination" shall mean any effort to use, combine, incorporate, link or integrate Licensed Software with any software created with or incorporating Qt Community Edition, or use Licensed Software for creation of any such software.
+2.25. "Qt Community Edition" shall mean  the open source version of Qt Software available under the terms of the GNU Lesser General Public License, version 2.1 or later ("LGPL") or the GNU General Public License, version 2.0 or later ("GPL"). For clarity, Qt Community Edition shall not be provided, governed or used under this Appendix.
+2.26. "Redistributables" shall mean the portions of Licensed Software as set forth in Exhibit 1 hereto that may be distributed pursuant to this Appendix in object code form only, including any relevant documentation. Where relevant, any reference to Licensed Software in this Appendix includes and refers  to Redistributables.
+2.27. "Renewal Term" shall mean an extension of the previous License Term as agreed between the Parties.
+2.28. "Submitted Modified Software" shall have the meaning as set forth in Section 4.2 of this Appendix.
+2.29. "Third-Party Software" shall have the meaning set forth in Section 6 of this Appendix.
+2.30. "Updates" shall mean a release or version of the Licensed Software containing bug fixes, error corrections and other changes that are generally made available to users of the Licensed Software that have contracted for Support. Updates are generally depicted as a change to the digits following the decimal in the Licensed Software version number. The Qt Company shall make Updates available to Customer under the Support. Updates shall be considered as part of the Licensed Software hereunder.
+2.31. "Upgrades" shall mean a release or version of the Licensed Software containing enhancements and new features and are generally depicted as a change to the first digit of the Licensed Software version number. In the event that Upgrades are provided to Customer under this Appendix, they shall be considered as part of the Licensed Software hereunder.
+
+3. OWNERSHIP
+3.1. Ownership of The Qt Company
+3.1.1. The Licensed Software is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. The Licensed Software is licensed, not sold.
+3.1.2. All of The Qt Company's Intellectual Property Rights are and shall remain the exclusive property of The Qt Company or its respective licensors . No rights to The Qt Company's Intellectual Property Rights are assigned or granted to Customer under this Appendix, except when and to the extent expressly specified herein.
+3.2. Ownership of Customer
+3.2.1. All of Customer's Intellectual Property Rights are and shall remain the exclusive property of Customer or its licensors respectively.
+3.2.2. Except to the extent set forth in this Appendix, all Intellectual Property Rights to the Modified Software, Applications and Devices (except to Redistributables included therein) shall remain with Customer.
+
+4. MODIFIED SOFTWARE
+4.1. Customer may create bug-fixes, error corrections, patches or modifications to the Licensed Software ("Modified Software"). To the extent that Customer's Modified Software breaks source or binary compatibility or other functionality with the Licensed Software, Customer acknowledges that The Qt Company's ability to provide Support may be prevented or limited and Customer's ability to make use of Updates may be restricted.
+4.2. Customer may, at its sole and absolute discretion, choose to submit Modified Software to The Qt Company ("Submitted Modified Software") in connection with Customer's Support request, service request or otherwise. In the event Customer does so, then, Customer hereby grants The Qt Company a sublicensable, assignable, irrevocable, perpetual, worldwide, non-exclusive, royalty-free and fully paid-up license, under all of Customer's Intellectual Property Rights, to reproduce, adapt, translate, modify, and prepare derivative works of, publicly display, publicly perform, sublicense, make available and distribute such Submitted Modified Software as The Qt Company sees fit at its free and absolute discretion.
+
+5. LICENSES GRANTED
+5.1. Development with Licensed Software
+5.1.1. Subject to the terms of the Agreement, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable license, valid for each License Term, to use, modify and copy the Licensed Software by Designated Users on the Development Platforms for the sole purposes of designing, developing, demonstrating and testing Application(s) and/or Devices, and to provide support and other services related to such Applications and Devices to End Customers. Each Application and/or Device can only include, incorporate or integrate contributions by such Designated Users who are duly licensed for the applicable Development Platform(s) and Deployment Platform(s) (i.e have a valid license for the appropriate Licensed Software product and only use one type of Qt Development License per Customer Application and/or Device(s)).
+5.1.2. Customer may install copies of the Licensed Software on five (5) computers per Designated User, provided that only Designated Users who have a valid Development License may use the Licensed Software.
+5.1.3. Customer may designate another Designated User to replace a then-current Designated User by notifying The Qt Company in writing, where such replacement is due to termination of employment, long-term absence or other permanent reason affecting Designated User's need for Licensed Software.
+5.1.4. Upon expiry of the initially agreed License Term, the respective License Term shall be automatically extended by one or more Renewal Term(s), unless and until either Party notifies the other Party in writing, that it does not wish to continue the License Term, such notification to be provided to the other Party no less than thirty (30) days before expiry of the respective License Term. The Qt Company shall, in good time before the due date for the above notification, remind the Customer on the coming Renewal Term. Unless otherwise agreed between the Parties, Renewal Term shall be equal to the length of the previous License Term, but no longer than thirty-six (36) months.
+5.1.5. Any such Renewal Term shall be subject to License Fees agreed between the Parties or, if no advance agreement exists, subject to The Qt Company's standard list pricing applicable at the commencement date of any such Renewal Term.
+5.2. Distribution of Applications
+5.2.1. Subject to the terms of the Agreement, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable, perpetual, royalty-free and revocable (only for Customer’s material breach of agreement) right and license to:
+(i) distribute, by itself or through its Contractors, Redistributables as installed, incorporated or integrated into Applications for execution on the Deployment Platforms; and
+(ii) grant perpetual and irrevocable sublicenses to Redistributables, as distributed hereunder, for End Customers solely to the extent necessary in order for the End Customers to use the Applications for their respective intended purposes.
+5.2.2. Right to distribute the Redistributables as part of an Application as provided herein is not royalty-bearing but is conditional upon the Application having been created, updated and maintained under a valid and duly paid Development License.
+5.3. Distribution of Devices
+5.3.1. Subject to the terms of the Agreement, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable, perpetual, revocable (only for Customer’s material breach of agreement), royalty-bearing right and license to:
+(i) distribute, by itself or through one or more tiers of Contractors, Redistributables as installed, incorporated or integrated, or intended to be installed, incorporated or integrated into Devices for execution on the Deployment Platforms; and
+(ii) grant perpetual and irrevocable sublicenses to Redistributables, as distributed hereunder, for End Customers solely to the extent necessary in order for the End Customers to use the Devices for their respective intended purposes.
+5.3.2. Right to distribute the Devices as provided herein is conditional upon (i) the Devices having been created, updated and maintained under a valid and duly paid Development License, and (ii) Customer having acquired corresponding Distribution Licenses at the time of distribution of any Devices to End Customers.
+5.4. Further Requirements
+5.4.1. The licenses granted in this Section 5 by The Qt Company to Customer are conditional and subject to Customer's compliance with the following terms:
+(i) Customer acknowledges that The Qt Company has separate products for the purpose of Applications and Devices respectively, where development and distribution of Devices is only allowed using the correct designated product. Customer shall ensure and bear the burden of proof that Customer is using a correct product entitling Customer to development and distribution of Devices;
+(ii) Customer shall not remove or alter any copyright, trademark or other proprietary rights notice(s) contained in any portion of the Licensed Software;
+(iii) Applications must add primary and substantial functionality to Licensed Software so as not to compete with the Licensed Software;
+(iv) Applications may not pass on functionality which in any way makes it possible for others to create software with Licensed Software; provided however that Customer may use Licensed Software’s scripting and QML ("Qt Quick") functionality solely in order to enable scripting, themes and styles that augment the functionality and appearance of the Application(s) without adding primary and substantial functionality to the Application(s);
+(v) Customer shall not use Licensed Software in any manner or for any purpose that infringes, misappropriates or otherwise violates any Intellectual Property Right or right of any third party, or that violates any applicable law;
+(vi) Customer shall not use The Qt Company's or any of its suppliers' names, logos, or trademarks to market Applications, except that Customer may use “Built with Qt” logo to indicate that an Application or Device was developed using Licensed Software;
+(vii) Customer shall not distribute, sublicense or disclose source code of Licensed Software to any third party (provided however that Customer may appoint employee(s) of Contractors and Affiliates as Designated Users to use Licensed Software pursuant to this Appendix).
+(viii) Customer shall not grant the End Customers a right to: (a) make copies of the Redistributables except when and to the extent required to use the Applications and/or Devices for their intended purpose; (b) modify the Redistributables or create derivative works thereof; (c) decompile, disassemble or otherwise reverse engineer Redistributables; or (d) redistribute any copy or portion of the Redistributables to any third party, except as part of the onward sale of the Application or Device on which the Redistributables are installed;
+(ix) Customer shall not, and shall cause that its Affiliates or Contractors shall not, use Licensed Software in any Prohibited Combination, unless Customer has received specific advance written permission from The Qt Company to do so. Absent such written permission, any and all distribution by Customer during the term of the Agreement of a hardware device or product: a) which incorporates or integrates any part of Licensed Software or Qt Community Edition; or b) where substantial functionality is provided by software built with Licensed Software or Qt Community Edition or otherwise depends on Licensed Software or Qt Community Edition, shall be considered to be Device distribution under this Appendix and shall be dependent on Customer’s compliance thereof (including but not limited to the obligation to pay applicable License Fees for such distribution). Notwithstanding the foregoing, Customer is entitled to use and combine Licensed Software with Permitted Software;
+(x) Customer shall cause all of its Affiliates, Contractors and End Customer entitled to make use of the licenses granted under this Appendix, to be contractually bound to comply with the relevant terms hereof and not to use the Licensed Software beyond the terms hereof nor for any purposes other than operating within the scope of their services for Customer. Customer shall be responsible for any and all actions and omissions of its Affiliates, Contractors, and End Customers relating to the Licensed Software and use thereof (including but not limited to payment of all applicable License Fees);
+(xi) Except when and to the extent explicitly provided in this Section 5, Customer shall not transfer, publish, disclose, display or otherwise make available the Licensed Software; and
+(xii) Customer shall not attempt or enlist a third party to conduct or attempt to conduct any of the above.
+5.4.2. The above terms shall not be applicable if and solely to the extent they conflict with any mandatory provisions of applicable laws.
+5.4.3. Any use of Licensed Software beyond the provisions of this Appendix is strictly prohibited and requires, at a minimum an additional license from The Qt Company (e.g. certain additional rights granted under software development kit “SDK” agreement with regard to limitations of Section 5.4.1 iv, vii or viii).
+5.5. Evaluation License
+5.5.1. Subject to the terms of this Appendix, The Qt Company grants to Customer a worldwide, non-exclusive, non-transferable license, valid for the Evaluation License Term to use the relevant Licensed Software product solely for Customer’s internal use to evaluate and determine whether the Licensed Software meets Customer's business requirements, specifically excluding any commercial use of the Licensed Software or any derived work thereof.
+5.5.2. Upon the expiry of the Evaluation License Term, Customer must either discontinue use of the relevant Licensed Software or acquire a commercial Development License specified herein.
+
+6. THIRD-PARTY SOFTWARE. The Licensed Software may provide links or access to third party libraries or code (collectively "Third-Party Software") to implement various functions. Third-Party Software does not, however, comprise part of the Licensed Software, but is provided to Customer complimentary and use thereof is discretionary for Customer. Third-Party Software will be listed in the ".../src/3rdparty" source tree delivered with the Licensed Software or documented in the Licensed Software, as such may be amended from time to time. Customer acknowledges that use or distribution of Third-Party Software is in all respects subject to applicable license terms of applicable third-party right holders.
+
+7. PRE-RELEASE CODE
+7.1. The Licensed Software may contain pre-release code and functionality, or sample code marked or otherwise stated with appropriate designation such as "Technology Preview", "Alpha", "Beta", "Experimental", "Sample", "Example" etc. ("Pre-Release Code").
+7.2. Such Pre-Release Code may be provided complimentary for  Customer, in order to provide experimental support or information for new platforms or preliminary versions of one or more new functionalities, or for other similar reasons. Pre-Release Code may not be at the level of performance and compatibility of a final, generally available, product offering.  Pre-Release Code may not operate correctly, may contain errors and may be substantially modified by The Qt Company prior to a commercial product release, if any. The Qt Company is under no obligation to make Pre-Release Code commercially available, or provide any Support or Updates relating thereto. To the maximum extent permitted by law, the Qt Company assumes no liability whatsoever regarding any Pre-Release Code and any use thereof is exclusively at Customer's own risk and expense.
+7.3.  Unless Licensed Software specifies different license terms for the respective Pre-Release Code, Customer is entitled to use such pre-release code pursuant to Section 5 of this Appendix, just like other Licensed Software.
+
+8. SUPPORT. Support is provided according to agreed support level and subject to applicable requirements and restrictions, as specified in the Appendix for Support Terms.
+
+9. FEES AND ORDERING: DISTRIBUTION LICENSES
+9.1. Distribution License Packs
+9.1.1. Unless otherwise agreed in writing, Distribution Licenses shall be purchased by way of Distribution License Packs.
+9.1.2. Upon due payment of the ordered Distribution License Pack(s), Customer will have an account of Distribution Licenses available for distributing the Redistributables in accordance with this Agreement.
+9.2. Each time Customer distributes a copy of Redistributables, one Distribution License is used and Customer's account of available Distribution Licenses is decreased accordingly.
+9.3. Customer may distribute copies of the Redistributables so long as Customer has Distribution Licenses remaining on its account.
+
+10. RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS
+10.1. Customer's Record-keeping
+10.1.1. Customer shall at all times during the term of the Agreement or validity of any of the licenses hereunder, whichever is later, and for a period of two (2) years thereafter, maintain Customer's Records in an accurate and up-to-date form. Customer's Records shall be adequate to reasonably enable The Qt Company to determine Customer's compliance with the provisions of the Agreement. The records shall conform to general good accounting practices.
+10.1.2. Customer shall, within thirty (30) days from receiving The Qt Company's request to that effect, deliver to The Qt Company a report based on Customer's Records, such report to contain information, in sufficient detail, on: (i) number and identity of users working with Licensed Software or Qt Community Edition, (ii) copies of Redistributables distributed by Customer during the most recent calendar quarter and/or any other term specified by The Qt Company, and (iii) any other information pertaining to Customer's compliance with the terms of the Agreement (e.g. information on products and/or projects relating to use of Distribution Licenses), as The Qt Company may reasonably require from time to time.
+10.2. The Qt Company's Audit Rights
+10.2.1. The Qt Company or an independent auditor acting on behalf of The Qt Company may, upon at least thirty (30) days' prior written notice and at The Qt Company expense, audit Customer with respect to Customer's use of the Licensed Software, but not more frequently than once during each six (6) month period. Such audit may be conducted by mail, electronic means or through an in-person visit to Customer's place of business. Any possible in-person audit shall be conducted during regular business hours at Customer's facilities, shall not unreasonably interfere with Customer's business activities and shall be limited in scope to verify Customer's compliance with the terms of the Agreement. The Qt Company or its independent auditor shall be entitled to inspect Customer's Records and conduct necessary interviews of Customer's relevant employees and Contractors. All Customer's Records and use thereof shall be subject to the obligation of confidentiality under the Agreement.
+10.2.2. If an audit reveals that Customer is using the Licensed Software beyond scope of the licenses Customer has paid for, Customer shall pay to The Qt Company any amounts owed for such unauthorized use within thirty (30) days from receipt of the corresponding invoice from The Qt Company.
+10.2.3. In addition, in the event the audit reveals a material violation of the terms of the Agreement (without limitation, either (i) underpayment of more than 10% of License Fees or 10,000 euros (whichever is more) or (ii) distribution of products, which include or result from Prohibited Combination, shall be deemed a material violation for purposes of this section), then Customer shall pay The Qt Company's reasonable cost of conducting such audit.
+
+11. TERMINATION
+11.1. Termination of Licenses
+11.1.1. The Qt Company may terminate Customer's rights to any and all Licensed Software (including access to Support), if Customer:
+(i) commits a material breach of the Agreement (including this Appendix) and has not remedied the breach within a reasonable period of time (which shall be no less than 30 days) of The Qt Company's written notice specifying the breach, or
+(ii) becomes bankrupt, insolvent or goes into liquidation or debt restructuring.
+11.2. Suspension of rights: Instead of termination, The Qt Company reserves the right to suspend or withhold grants of any and all rights to the Licensed Software (including Support), should Customer fail to make payment in timely fashion or otherwise violate or is reasonably suspected of violating its obligations under the Agreement and/or this Appendix, and where such violation or breach is not cured within ten (10) business days following The Qt Company's written notice thereof.
+11.3. Parties  Rights and Duties upon Termination
+11.3.1. Upon expiry or termination of the Development Licenses, Customer shall cease and shall cause all Designated Users (including those of its Affiliates' and Contractors') to cease using the relevant Licensed Software.
+11.3.2. Upon such expiry or termination of Development Licenses, Customer shall destroy or return to The Qt Company all copies of the respective Licensed Software and all related materials and will certify the same by Customer's duly authorized officer to The Qt Company upon its request, provided however that Customer may retain and utilize such copies of the Licensed Software to the extent required to provide Customer's continued support to End Customers,  for archiving purposes or as is required under applicable law.
+11.3.3. Distribution Licenses are perpetual and, therefore, Customer's distribution rights hereunder shall only terminate upon The Qt Company's termination of Distribution Licenses due to Customer's material breach as set forth in Section 11.1.1(i) of this Appendix. In case of such termination by The Qt Company due to Customer's material breach, Customer must cease any distribution of Applications and Devices at the effective date of termination.
+11.3.4. Expiry or termination of any of Customer's licenses hereunder for any reason whatsoever shall not:
+(i) relieve Customer of its obligation to pay any License Fees accrued or payable to The Qt Company prior to the effective date of termination, and Customer pay to The Qt Company all such fees within 30 days from the effective date of termination of the licenses;
+(ii) relieve Customer of its obligation to ensure that Applications and Devices (including those already distributed) remain in compliance with the terms of the Agreement; nor
+(iii) affect any rights of End Customer to continue use of Applications and Devices (and therein incorporated Redistributables).
+11.4. Extension of Rights under Special Circumstances. In the event that, during the applicable License Term, The Qt Company is declared bankrupt under a final, non-cancellable decision by relevant court of law, and the Agreement is not, at the date of expiry of the Development License(s), assigned to a party who has assumed The Qt Company's position as a legitimate licensor of Licensed Software under the Agreement, then all valid Development Licenses possessed by Customer at such date of expiry, and which Customer has not notified for expiry, shall be extended to be valid in perpetuity under the terms of the Agreement. Any such extension shall not apply to The Qt Company's Support obligations.
+
+EXHIBIT 1, Licensed Software
+At the time of conclusion of this Appendix, the latest available version of Licensed Software includes the software libraries and tools set forth in Exhibit 1 (as provided below), depending on which product(s) Customer has purchased under the relevant Purchase Document.
+The modules and tools are specific to each product version respectively and may vary from version to version. Modules and tools included in the latest publicly available version of the respective product at any given time are listed in Exhibit 1 of  https://www.qt.io/terms-conditions/qt-dev-framework/exhibit-1. If a new version of Licensed Software does not include a module or tool present in an older version which Customer is entitled to use under a valid license from The Qt Company, then Customer will continue to have such right during the validity of Customer's license to relevant Licensed Software.  In the event a new version of the Licensed Software adds modules or tools to any previous version(s), Customer's rights will extend to cover also such additional modules and tools.
+
+EXHIBIT 2 - Small Business Terms
+1. This Exhibit applies to entities that qualify as a Qualified Small Business (defined below) and provides additional terms and conditions applicable to small business pricing and licensing. In the event that Customer is a Qualified Small Business and there is any conflict between the terms of this Exhibit and any other terms of the Agreement, the terms in this Exhibit shall take precedence.
+
+2. APPLICABILITY FOR SMALL BUSINESS LICENSES. Any small business discounts applied require that Customer (including any Customer Affiliates or group entities) has an annual revenue (including annual capital funding) below 1 Million EUR, or the equivalent thereof, as approved by The Qt Company (each, a "Qualified Small Business"). The annual revenue, including funding, must be evidenced upon request by business records and approved by The Qt Company in its reasonable discretion.
+
+3. SUPPORT. Support is limited to: (i) Install Support; and (ii) for any other Standard Support issue, five (5) support tickets annually.
+
+4. LIMITATION ON NUMBER OF SMALL BUSINESS DEVELOPER LICENSES. Qualified Small Business discounts and purchasing structure may be applied to a maximum of  three discounted developer licenses (either ADE or DCP) per Qualified Small Business. Any additional licenses purchased will be at The Qt Company list price in effect at the time.
+
+5. LIMITATION FOR NUMBER OF INSTALLATIONS. Customer may install copies of the Licensed Software on two (2) computers per Designated User, provided that only the Designated Users who have a valid Development License may use the Licensed Software.
+
+6. CONDITIONAL WAIVER OF DISTRIBUTION LICENSES. For Qualified Small Businesses, the Agreement requirements to purchase Distribution Licenses for Devices shall apply only when Customer ceases to be a Qualified Small Business (e.g., when annual revenue threshholds  are bypassed).
+
+7. ADDITIONAL TERMS FOR RENEWALS. The initial subscription purchase term for Qualified Small Business Licenses is twelve (12) months. Upon expiration of the initial twelve (12) month term and unless terminated in accordance with the Agreement, the Licenses will automatically renew for additional twelve (12) month terms with applicable Qualified Small Business discounts. If Customer ceases to be a Qualified Small Business, renewal pricing shall be at The Qt Company list price in effect at the time of renewal, or as agreed in writing between the parties.
+
+8. ADDITIONAL AUDIT RIGHTS. In addition to the audit rights set forth in the Agreement, The Qt Company reserves the right to audit Customer financial records in order to determine whether Customer is a Qualified Small Business.
diff --git a/.flake8 b/.flake8
new file mode 100644 (file)
index 0000000..ef86603
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,9 @@
+[flake8]
+ignore = E115,E265,W503
+max-line-length = 100
+exclude = rc_*.py,*_rc.py,ui_*.py
+per-file-ignores =
+    # for init_test_paths() hack
+    *_test_*.py:E402
+    __init__.py:F401,E402
+
index 57f2286da5206011a63a5b7f15a7d55470c43f5e..3d19a40b45948bc2f670fd2676d12a09405992b0 100644 (file)
@@ -12,5 +12,8 @@
         "*_test.py"
     ],
     "python.testing.pytestEnabled": false,
-    "python.testing.unittestEnabled": true
+    "python.testing.unittestEnabled": true,
+    "flake8.args": [
+        "--config=.flake8"
+    ]
 }
index 449a40babf1d6506967fd8118b51abd2517f73c6..cdeb0398bc2adcc243f81c76573b1c36019e0810 100644 (file)
--- a/README.md
+++ b/README.md
@@ -113,6 +113,8 @@ using `setup.py build`:
    or macOS only).
  * `--verbose-build`, will output the compiler invocation with command line
    arguments, etc.
+ * `--disable-pyi`, will suppress the generation of .pyi files. This allows
+   debugging when the project builds but the pyi generator complains.
 
 ## Requirements
 
index 0ed65d25c789737a5909727d7b964f5dde9cccaf..3247a550d66471f220c80354687d264cf7812ed9 100644 (file)
@@ -22,6 +22,7 @@ it includes the following Qt modules:
 * QtBluetooth
 * QtCharts
 * QtDataVisualization
+* QtGraphs
 * QtMultimedia
 * QtMultimediaWidgets
 * QtNetworkAuth
@@ -32,7 +33,10 @@ it includes the following Qt modules:
 * QtScxml
 * QtSensors
 * QtSerialPort
+* QtSerialBus
+* QtSpatialAudio
 * QtStateMachine
+* QtTextToSpeech
 * QtVirtualKeyboard
 * QtWebChannel
 * QtWebEngineCore
@@ -42,6 +46,8 @@ it includes the following Qt modules:
 * QtPdf
 * QtPdfWidgets
 * QtHttpServer
+* QtLocation
+* QtAsyncio
 
 ### Documentation and Bugs
 
index 60cc3f955563450789fc5c56a70045bfdcea5817..5f0183f5bb441c53ce6540e75936844d0d1ad0f9 100644 (file)
     win32
 [QtQml::bug_825]
     py3.8       # bug in typeobject::type_mro_modified, fix in 3.9
-    py3.9       # fixed in 3.9.12
-    py3.10      # fixed in 3.10.4
+    py3.9.0
+    py3.9.1
+    py3.9.2
+    py3.9.3
+    py3.9.4
+    py3.9.5
+    py3.9.6
+    py3.9.7
+    py3.9.8
+    py3.9.9
+    py3.9.10
+    py3.9.11
+    # fixed in 3.9.12
+    py3.10.0
+    py3.10.1
+    py3.10.2
+    py3.10.3
+    # fixed in 3.10.4
 # PYSIDE-535: These errors are still present. Please try to remove one :)
 [sample::mixed_mi]
     pypy
index 314b6d30554f2db4060ac16b11e61f98d9f0eb52..cc77c40bb0406429f452b5094be02588e4614d59 100644 (file)
@@ -611,9 +611,9 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             # embedding_generator.py. Pass it as a separate option.
             cmake_cmd.append(f"-DQFP_PYTHON_HOST_PATH={sys.executable}")
         else:
-            cmake_cmd.append(f"-DPYTHON_EXECUTABLE={self.py_executable}")
-            cmake_cmd.append(f"-DPYTHON_INCLUDE_DIR={self.py_include_dir}")
-            cmake_cmd.append(f"-DPYTHON_LIBRARY={self.py_library}")
+            cmake_cmd.append(f"-DPython_EXECUTABLE={self.py_executable}")
+            cmake_cmd.append(f"-DPython_INCLUDE_DIR={self.py_include_dir}")
+            cmake_cmd.append(f"-DPython_LIBRARY={self.py_library}")
 
         # If a custom shiboken cmake config directory path was provided, pass it to CMake.
         if OPTION["SHIBOKEN_CONFIG_DIR"] and config.is_internal_pyside_build():
@@ -674,10 +674,7 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             else:
                 log.warning('numpy include directory was not found.')
 
-        if self.build_type.lower() == 'debug':
-            if not self.is_cross_compile:
-                cmake_cmd.append(f"-DPYTHON_DEBUG_LIBRARY={self.py_library}")
-        else:
+        if self.build_type.lower() != 'debug':
             if OPTION['NO_STRIP']:
                 cmake_cmd.append("-DQFP_NO_STRIP=1")
             if OPTION['NO_OVERRIDE_OPTIMIZATION_FLAGS']:
@@ -695,12 +692,14 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
                              "(default yes if applicable, i.e. Python "
                              "version >= 3.8 and release build if on Windows)")
 
+        if OPTION["DISABLE_PYI"]:
+            cmake_cmd.append("-DDISABLE_PYI=yes")
+
         if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE:
             cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
         else:
             cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF")
 
-
         if OPTION['COMPILER_LAUNCHER']:
             compiler_launcher = OPTION['COMPILER_LAUNCHER']
             cmake_cmd.append(f"-DCMAKE_C_COMPILER_LAUNCHER={compiler_launcher}")
@@ -859,8 +858,7 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
                     if OPTION["LOG_LEVEL"] == LogLevel.VERBOSE and self.make_generator == "Ninja":
                         make_doc_cmd.append("-v")
                     if run_process(make_doc_cmd) != 0:
-                        raise SetupError("Error generating documentation "
-                                                  f"for {extension}")
+                        raise SetupError(f"Error generating documentation for {extension}")
                 else:
                     log.info("Sphinx not found, skipping documentation build")
         else:
@@ -1006,8 +1004,8 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
             # (lib/libclang.lib), whereas we want to copy the shared
             # library (bin/libclang.dll).
             clang_lib_path = Path(re.sub(r'lib/libclang.lib$',
-                                    'bin/libclang.dll',
-                                    clang_lib_path))
+                                         'bin/libclang.dll',
+                                         clang_lib_path))
         else:
             clang_lib_path = Path(clang_lib_path)
             # shiboken6 links against libclang.so.6 or a similarly
@@ -1095,6 +1093,7 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
 
         message = "Patched rpath to '$ORIGIN/' in"
         if sys.platform.startswith('linux'):
+
             def rpath_cmd(srcpath):
                 final_rpath = ''
                 # Command line rpath option takes precedence over
@@ -1113,6 +1112,7 @@ class PysideBuild(_build, CommandMixin, BuildInfoCollectorMixin):
 
         elif sys.platform == 'darwin':
             message = "Updated rpath in"
+
             def rpath_cmd(srcpath):
                 final_rpath = ''
                 # Command line rpath option takes precedence over
@@ -1274,7 +1274,7 @@ class PysideRstDocs(Command, CommandMixin):
             if self.name == PYSIDE:
                 self.sphinx_src = self.out_dir / "rst"
                 example_gallery = config.setup_script_dir / "tools" / "example_gallery" / "main.py"
-                assert(example_gallery.is_file())
+                assert example_gallery.is_file()
                 example_gallery_cmd = [sys.executable, os.fspath(example_gallery)]
                 if OPTION["LOG_LEVEL"] == LogLevel.QUIET:
                     example_gallery_cmd.append("--quiet")
index e49dd61cc89c02abffa1130bd48d9d312fc0dbc8..6c6acd5882e7976dc0c48b6bfc52284a0b97d4b5 100644 (file)
@@ -18,12 +18,13 @@ _AVAILABLE_MKSPECS = ["ninja", "msvc", "mingw"] if sys.platform == "win32" else
 ADDITIONAL_OPTIONS = """
 Additional options:
   --limited-api                        Use Limited API [yes/no]
-  ---macos-use-libc++                  Use libc++ on macOS
+  --macos-use-libc++                   Use libc++ on macOS
   --snapshot-build                     Snapshot build
   --package-timestamp                  Package Timestamp
   --cmake-toolchain-file               Path to CMake toolchain to enable cross-compiling
   --shiboken-host-path                 Path to host shiboken package when cross-compiling
   --qt-host-path                       Path to host Qt installation when cross-compiling
+  --disable-pyi                        Disable .pyi file generation
 """
 
 
@@ -159,6 +160,7 @@ OPTION = {
     "VERBOSE_BUILD": has_option('verbose-build'),
     "SNAPSHOT_BUILD": has_option("snapshot-build"),
     "LIMITED_API": option_value("limited-api"),
+    "DISABLE_PYI": has_option("disable-pyi"),
     "PACKAGE_TIMESTAMP": option_value("package-timestamp"),
     # This is used automatically by setuptools.command.install object, to
     # specify the final installation location.
@@ -442,8 +444,8 @@ class CommandMixin(object):
         # qtpaths is available. This happens when building the host
         # tools in the overall cross-building process.
         use_cmake = False
-        if (using_cmake_toolchain_file or
-                (not self.qmake and not self.qtpaths and self.qt_target_path)):
+        if (using_cmake_toolchain_file or (not self.qmake
+                                           and not self.qtpaths and self.qt_target_path)):
             use_cmake = True
 
         QtInfo().setup(qtpaths_abs_path, self.cmake, qmake_abs_path,
@@ -541,7 +543,8 @@ class CommandMixin(object):
         # explicitly. This is to help with the building of host tools
         # while cross-compiling.
         # Skip this process for the 'build_rst_docs' command
-        if not self.is_cross_compile and not self.qt_target_path and 'build_rst_docs' not in sys.argv:
+        if not (self.is_cross_compile and not self.qt_target_path
+                and 'build_rst_docs' not in sys.argv):
             # Enforce usage of qmake in QtInfo if it was given explicitly.
             if self.qmake:
                 self.has_qmake_option = True
index 835cc4dd095f025786106589bf5e8b944c00d422..cb388e8aaefb35c392a8c4f7d89520b188209a15 100644 (file)
@@ -19,7 +19,6 @@ def prepare_standalone_package_linux(pyside_build, _vars, cross_build=False, is_
     copy_qml = True
     copy_translations = True
     copy_qt_conf = True
-    should_copy_icu_libs = True
 
     log.info("Copying files...")
 
@@ -29,7 +28,6 @@ def prepare_standalone_package_linux(pyside_build, _vars, cross_build=False, is_
         copy_qml = False
         copy_translations = False
         copy_qt_conf = False
-        should_copy_icu_libs = False
 
     # <qt>/lib/* -> <setup>/{st_package_name}/Qt/lib
     destination_dir = Path("{st_build_dir}/{st_package_name}".format(**_vars))
@@ -55,7 +53,7 @@ def prepare_standalone_package_linux(pyside_build, _vars, cross_build=False, is_
             _filter=accepted_modules,
             recursive=False, _vars=_vars, force_copy_symlinks=True)
 
-    if should_copy_icu_libs and not cross_build and not is_android:
+    if not cross_build and not is_android:
         # Check if ICU libraries were copied over to the destination
         # Qt libdir.
         maybe_icu_libs = find_files_using_glob(destination_qt_lib_dir, "libicu*")
index 6b4a3e95df03e0b9ef807be3b51edc059a4c4196..17c4ba34f98dc30b0fc2e488e83e480c1d3aa226 100644 (file)
@@ -126,6 +126,7 @@ def prepare_packages_posix(pyside_build, _vars, cross_build=False):
 
             if sys.platform.startswith("linux"):
                 scripts.append("android_deploy.py")
+                scripts.append("requirements-android.txt")
                 script_dirs.extend(["deploy_lib/android",
                                     "deploy_lib/android/recipes/PySide6",
                                     "deploy_lib/android/recipes/shiboken6",])
@@ -140,7 +141,8 @@ def prepare_packages_posix(pyside_build, _vars, cross_build=False):
                 src = f"{{install_dir}}/bin/{script_dir}"
                 target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script_dir}"
                 # Exclude subdirectory tests
-                copydir(src, target, _filter=["*.py", "*.spec", "*.jpg"], recursive=False, _vars=_vars)
+                copydir(src, target, _filter=["*.py", "*.spec", "*.jpg", "*.icns", "*.ico"],
+                        recursive=False, _vars=_vars)
 
             # <install>/bin/* -> {st_package_name}/
             executables.extend(copydir(
index 543ee98803a765dc9f3d42c019a64f9dd0e7cb5c..c3b19d5a198641b5e2f86088f968cade32b62360 100644 (file)
@@ -120,7 +120,8 @@ def prepare_packages_win32(pyside_build, _vars):
             src = f"{{install_dir}}/bin/{script_dir}"
             target = f"{{st_build_dir}}/{{st_package_name}}/scripts/{script_dir}"
             # Exclude subdirectory tests
-            copydir(src, target, _filter=["*.py", "*.spec"], recursive=False, _vars=_vars)
+            copydir(src, target, _filter=["*.py", "*.spec", "*.jpg", "*.icns", "*.ico"],
+                    recursive=False, _vars=_vars)
 
         # <install>/bin/*.exe,*.dll -> {st_package_name}/
         filters = ["pyside*.exe", "pyside*.dll"]
index 15aa741efceeaa0589bbebf7c2c0fcd045585648..ec66b957f51051e05053ccc22eae9913b33fc673 100644 (file)
@@ -157,7 +157,7 @@ def wheel_files_pyside_essentials() -> List[ModuleData]:
         module_QtWidgets(),
         module_QtHelp(),
         module_QtNetwork(),
-        module_QtConcurent(),
+        module_QtConcurrent(),
         module_QtDBus(),
         module_QtDesigner(),
         module_QtOpenGL(),
@@ -383,7 +383,7 @@ def module_QtBluetooth() -> ModuleData:
     return data
 
 
-def module_QtConcurent() -> ModuleData:
+def module_QtConcurrent() -> ModuleData:
     data = ModuleData("Concurrent")
 
     return data
index 8a1c4166f666dd71fa36e2ef0b1ba9e324365c71..2bf2feb276b00834c4c3ede48dafbe348d0bbce2 100644 (file)
@@ -1,6 +1,6 @@
 product_dependency:
   ../../qt/qt5:
-    ref: "e6ff9950e1d0365d2c4f231fd1b6fe194144fd52"
+    ref: "15b7e7434fd79334a5cf071e36cee7663fe1fb45"
 dependency_source: supermodule
 dependencies: [
       "../../qt/qt3d",
index ab1c45eff9d661acce3193a6f24965ed5c36de2f..8cb3c1d5086dcc0e73926f8b0355f1d7f0adb465 100644 (file)
@@ -250,7 +250,7 @@ instructions:
             equals_value: Linux
     - type: EnvironmentVariable
       variableName: interpreter
-      variableValue: "python3.8"
+      variableValue: "python3.11"
       enable_if:
          condition: property
          property: host.osVersion
index de3b9a8cd30bc4b8b8fbbe5952439007d316dc11..0f278dbcf40b3abe0447c2d069b7ba192be77507 100644 (file)
@@ -12,8 +12,8 @@ from build_scripts.log import log
 from build_scripts.options import has_option, option_value
 from build_scripts.utils import (expand_clang_variables, get_ci_qtpaths_path,
                                  get_qtci_virtualEnv,
-                                 parse_cmake_conf_assignments_by_key, remove_tree,
-                                 run_instruction)
+                                 parse_cmake_conf_assignments_by_key,
+                                 remove_tree, run_instruction)
 
 log.setLevel(logging.INFO)
 
@@ -27,20 +27,22 @@ CI_ENV_INSTALL_DIR = option_value("instdir")
 CI_ENV_AGENT_DIR = option_value("agentdir")
 CI_COMPILER = option_value("compiler")
 CI_USE_SCCACHE = option_value("compiler-launcher")
-CI_INTEGRATION_ID = option_value("coinIntegrationId") or str(calendar.timegm(datetime.datetime.now().timetuple()))
+CI_INTEGRATION_ID = option_value("coinIntegrationId") or str(
+    calendar.timegm(datetime.datetime.now().timetuple())
+)
 CI_FEATURES = []
 _ci_features = option_value("features")
 if _ci_features is not None:
-    for f in _ci_features.split(', '):
+    for f in _ci_features.split(", "):
         CI_FEATURES.append(f)
 CI_RELEASE_CONF = has_option("packaging")
 CI_TEST_PHASE = option_value("phase")
-if CI_TEST_PHASE not in ["ALL", "BUILD", "WHEEL"]:
+if CI_TEST_PHASE not in ["ALL", "BUILD"]:
     CI_TEST_PHASE = "ALL"
 
 
 def get_current_script_path():
-    """ Returns the absolute path containing this script. """
+    """Returns the absolute path containing this script."""
     try:
         this_file = __file__
     except NameError:
@@ -64,8 +66,8 @@ def is_snapshot_build():
     pyside_project_dir = os.path.join(setup_script_dir, "sources", "pyside6")
 
     d = parse_cmake_conf_assignments_by_key(pyside_project_dir)
-    release_version_type = d.get('pyside_PRE_RELEASE_VERSION_TYPE')
-    pre_release_version = d.get('pyside_PRE_RELEASE_VERSION')
+    release_version_type = d.get("pyside_PRE_RELEASE_VERSION_TYPE")
+    pre_release_version = d.get("pyside_PRE_RELEASE_VERSION")
     if pre_release_version and release_version_type:
         return True
     return False
@@ -75,7 +77,9 @@ def call_setup(python_ver, phase):
     print("call_setup")
     print("python_ver", python_ver)
     print("phase", phase)
-    _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH)
+    _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(
+        python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH
+    )
 
     if phase in ["BUILD"]:
         remove_tree(_env, True)
@@ -84,7 +88,10 @@ def call_setup(python_ver, phase):
         python3 = "python3"
         if sys.platform == "win32":
             python3 = os.path.join(os.getenv("PYTHON3_PATH"), "python.exe")
-        run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"], "Failed to pin virtualenv")
+        run_instruction(
+            [python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+            "Failed to pin virtualenv",
+        )
         # installing to user base might not be in PATH by default.
         env_path = os.path.join(site.USER_BASE, "bin")
         v_env = os.path.join(env_path, "virtualenv")
@@ -99,17 +106,17 @@ def call_setup(python_ver, phase):
             v_env = "virtualenv"
         run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
         # When the 'python_ver' variable is empty, we are using Python 2
-        # Pip is always upgraded when CI template is provisioned, upgrading it in later phase may cause perm issue
-        run_instruction([str(env_pip), "install", "-r", "requirements.txt"], "Failed to install dependencies")
+        # Pip is always upgraded when CI template is provisioned,
+        # upgrading it in later phase may cause perm issue
+        run_instruction(
+            [str(env_pip), "install", "-r", "requirements.txt"], "Failed to install dependencies"
+        )
 
     cmd = [env_python, "-u", "setup.py"]
     if phase in ["BUILD"]:
         cmd += ["build", "--standalone", "--unity"]
-    elif phase in ["WHEEL"] or CI_RELEASE_CONF:
-        cmd += ["bdist_wheel", "--reuse-build", "--standalone", "--skip-cmake", "--skip-make-install", "--only-package"]
 
-    cmd += ["--build-tests",
-            "--log-level=verbose"]
+    cmd += ["--build-tests", "--log-level=verbose"]
 
     if CI_TARGET_ARCH == "X86_64-ARM64":
         cmd += ["--macos-arch='x86_64;arm64'"]
@@ -137,20 +144,20 @@ def call_setup(python_ver, phase):
     env = os.environ
     run_instruction(cmd, "Failed to run setup.py for build", initial_env=env)
 
-if __name__ == "__main__":
 
+if __name__ == "__main__":
     # Remove some environment variables that impact cmake
-    arch = '32' if CI_TARGET_ARCH == 'X86' else '64'
+    arch = "32" if CI_TARGET_ARCH == "X86" else "64"
     expand_clang_variables(arch)
-    for env_var in ['CC', 'CXX']:
+    for env_var in ["CC", "CXX"]:
         if os.environ.get(env_var):
             del os.environ[env_var]
     python_ver = "3"
-    if CI_TARGET_OS in ["Linux"] and CI_HOST_ARCH !="aarch64":
-        python_ver = "3.8"
+    if CI_TARGET_OS in ["Linux"] and CI_HOST_ARCH != "aarch64":
+        python_ver = "3.11"
     wheel_package_dir = "qfpa-p3.6"
     if CI_TARGET_OS in ["Windows"]:
-        if (os.environ.get('HOST_OSVERSION_COIN')).startswith('windows_10'):
+        if (os.environ.get("HOST_OSVERSION_COIN")).startswith("windows_10"):
             python_ver = "3.10.0"
         else:
             python_ver = "3.8.1"
@@ -160,8 +167,4 @@ if __name__ == "__main__":
     if os.environ.get("QTEST_ENVIRONMENT") == "ci" and sys.platform == "win32":
         signing_dir = str(os.environ.get("PYSIDE_SIGNING_DIR"))
         print("Check for signing dir " + signing_dir)
-        assert(os.path.isdir(signing_dir))
-    if CI_TEST_PHASE in ["ALL", "WHEEL"] and sys.platform != "win32":
-        # "Old" Windows wheels won't be signed anyway so there is no need to
-        # create those, so that we don't accidentally release those.
-        call_setup(python_ver, "WHEEL")
+        assert os.path.isdir(signing_dir)
index 03959a53682a87e4e10c50b15042a1f97b7a2a40..219fcf377e94155e4056fa51231f32e1dc5b6f59 100644 (file)
@@ -6,7 +6,7 @@ import site
 import sys
 
 from build_scripts.log import log
-from build_scripts.options import has_option, log, option_value
+from build_scripts.options import has_option, option_value
 from build_scripts.utils import (expand_clang_variables, get_ci_qmake_path,
                                  get_qtci_virtualEnv, remove_tree, run_instruction)
 
@@ -30,7 +30,8 @@ CI_RELEASE_CONF = has_option("packaging")
 
 
 def call_testrunner(python_ver, buildnro):
-    _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH, CI_TARGET_ARCH)
+    _pExe, _env, env_pip, env_python = get_qtci_virtualEnv(python_ver, CI_HOST_OS, CI_HOST_ARCH,
+                                                           CI_TARGET_ARCH)
     remove_tree(_env, True)
     # Pinning the virtualenv before creating one
     # Use pip3 if possible while pip seems to install the virtualenv to wrong dir in some OS
@@ -42,9 +43,11 @@ def call_testrunner(python_ver, buildnro):
     if CI_HOST_OS == "MacOS" and CI_HOST_ARCH == "ARM64":
         v_env = "virtualenv"
         run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
-        run_instruction([env_pip, "install", "-r", "requirements.txt"], "Failed to install dependencies")
+        run_instruction([env_pip, "install", "-r", "requirements.txt"],
+                        "Failed to install dependencies")
     else:
-        run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"], "Failed to pin virtualenv")
+        run_instruction([python3, "-m", "pip", "install", "--user", "virtualenv==20.7.2"],
+                        "Failed to pin virtualenv")
         # installing to user base might not be in PATH by default.
         env_path = os.path.join(site.USER_BASE, "bin")
         v_env = os.path.join(env_path, "virtualenv")
@@ -59,8 +62,10 @@ def call_testrunner(python_ver, buildnro):
             v_env = "virtualenv"
         run_instruction([str(v_env), "-p", str(_pExe), str(_env)], "Failed to create virtualenv")
         # When the 'python_ver' variable is empty, we are using Python 2
-        # Pip is always upgraded when CI template is provisioned, upgrading it in later phase may cause perm issue
-        run_instruction([env_pip, "install", "-r", "requirements.txt"], "Failed to install dependencies")
+        # Pip is always upgraded when CI template is provisioned,
+        # upgrading it in later phase may cause perm issue
+        run_instruction([env_pip, "install", "-r", "requirements.txt"],
+                        "Failed to install dependencies")
         # Install distro to replace missing platform.linux_distribution() in python3.8
         run_instruction([env_pip, "install", "distro"], "Failed to install distro")
 
@@ -73,18 +78,8 @@ def call_testrunner(python_ver, buildnro):
     # Try to install built wheels, and build some buildable examples.
     if CI_RELEASE_CONF:
         wheel_tester_path = os.path.join("testing", "wheel_tester.py")
-        # We create wheels differently in Qt CI with Windows and there are no "old" wheels
-        if CI_HOST_OS != "Windows":
-            # Run the test for the old set of wheels
-            cmd = [env_python, wheel_tester_path, qmake_path]
-            run_instruction(cmd, "Error while running wheel_tester.py on old wheels")
-
-            # Uninstalling the other wheels
-            run_instruction([env_pip, "uninstall", "shiboken6", "shiboken6_generator", "pyside6", "-y"],
-                            "Failed to uninstall old wheels")
-
         # Run the test for the new set of wheels
-        cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist_new", "--new"]
+        cmd = [env_python, wheel_tester_path, qmake_path, "--wheels-dir=dist", "--new"]
         run_instruction(cmd, "Error while running wheel_tester.py on new wheels")
 
 
@@ -106,7 +101,7 @@ def run_test_instructions():
         else:
             call_testrunner("3.8.1", str(testRun))
     elif CI_HOST_OS == "Linux":
-        call_testrunner("3.8", str(testRun))
+        call_testrunner("3.11", str(testRun))
     else:
         call_testrunner("3", str(testRun))
 
index 46e218f1f741138d6e568f47deb0782444dcf6d5..1b34fa402fe7f1b508c6d37073ee362e7997a522 100644 (file)
@@ -12,6 +12,8 @@ from shutil import copy, rmtree, copytree
 from typing import List, Optional, Tuple
 
 import build  # type: ignore
+import pyproject_hooks
+import build_scripts.wheel_files
 from build_scripts.wheel_files import (ModuleData,  # type: ignore
                                        set_pyside_package_path,
                                        wheel_files_pyside_addons,
@@ -20,6 +22,7 @@ from build_scripts.utils import available_pyside_tools
 
 
 PACKAGE_FOR_WHEELS = "package_for_wheels"
+PYSIDE_DESCRIPTION = "Python bindings for the Qt cross-platform application and UI framework"
 
 
 @dataclass
@@ -134,8 +137,28 @@ def get_platform_tag() -> str:
 def generate_pyproject_toml(artifacts: Path, setup: SetupData) -> str:
     content = None
 
+    _name = setup.name
     _tag = get_platform_tag()
 
+    _console_scripts = ""
+    if setup.console_scripts:
+        _formatted_console_scripts = "\n".join(setup.console_scripts)
+        _console_scripts = f"[project.scripts]\n{_formatted_console_scripts}"
+
+    # Installing dependencies
+    _dependencies = []
+    if _name in ("PySide6", "PySide6_Examples"):
+        _dependencies.append(f"shiboken6=={setup.version[0]}")
+        _dependencies.append(f"PySide6_Essentials=={setup.version[0]}")
+        _dependencies.append(f"PySide6_Addons=={setup.version[0]}")
+    elif _name == "PySide6_Essentials":
+        _dependencies.append(f"shiboken6=={setup.version[0]}")
+    elif _name == "PySide6_Addons":
+        _dependencies.append(f"shiboken6=={setup.version[0]}")
+        _dependencies.append(f"PySide6_Essentials=={setup.version[0]}")
+    elif _name == "shiboken6_generator":
+        _dependencies.append(f"shiboken6=={setup.version[0]}")
+
     with open(artifacts / "pyproject.toml.base") as f:
         content = (
             f.read()
@@ -144,8 +167,9 @@ def generate_pyproject_toml(artifacts: Path, setup: SetupData) -> str:
             .replace("PROJECT_DESCRIPTION", f'"{setup.description}"')
             .replace("PROJECT_README", f'"{setup.readme}"')
             .replace("PROJECT_TAG", f'"{_tag}"')
+            .replace("PROJECT_SCRIPTS", _console_scripts)
+            .replace("PROJECT_DEPENDENCIES", f"{_dependencies}")
         )
-
     return content
 
 
@@ -164,18 +188,6 @@ def generate_setup_py(artifacts: Path, setup: SetupData):
     else:
         fext = "Shiboken"
 
-    # Installing dependencies
-    install_requires = []
-    if name in ("PySide6", "PySide6_Examples"):
-        install_requires.append(f"shiboken6=={setup.version[0]}")
-        install_requires.append(f"PySide6_Essentials=={setup.version[0]}")
-        install_requires.append(f"PySide6_Addons=={setup.version[0]}")
-    elif _name == "PySide6_Essentials":
-        install_requires.append(f"shiboken6=={setup.version[0]}")
-    elif _name == "PySide6_Addons":
-        install_requires.append(f"shiboken6=={setup.version[0]}")
-        install_requires.append(f"PySide6_Essentials=={setup.version[0]}")
-
     # For special wheels based on 'PySide6'
     # we force the name to be PySide6 for the package_name,
     # so we can take the files from that packaged-directory
@@ -186,8 +198,6 @@ def generate_setup_py(artifacts: Path, setup: SetupData):
         content = f.read().format(
             name=_name,
             fake_ext=fext,
-            install=install_requires,
-            console_scripts={"console_scripts": setup.console_scripts},
         )
 
     return content
@@ -200,8 +210,8 @@ def wheel_shiboken_generator(package_path: Path) -> Tuple[SetupData, None]:
         description="Python/C++ bindings generator",
         readme="README.shiboken6-generator.md",
         console_scripts=[
-            "shiboken6 = shiboken6_generator.scripts.shiboken_tool:main",
-            "shiboken6-genpyi = shiboken6_generator.scripts.shiboken_tool:genpyi",
+            'shiboken6 = "shiboken6_generator.scripts.shiboken_tool:main"',
+            'shiboken6-genpyi = "shiboken6_generator.scripts.shiboken_tool:genpyi"',
         ],
     )
 
@@ -229,16 +239,16 @@ def wheel_pyside6_essentials(package_path: Path) -> Tuple[SetupData, List[Module
     # Also, the tool should not exist in any other platform than Linux
     _console_scripts = []
     if ("android_deploy" in _pyside_tools) and sys.platform.startswith("linux"):
-        _console_scripts = ["pyside6-android-deploy = PySide6.scripts.pyside_tool:android_deploy"]
+        _console_scripts = ['pyside6-android-deploy = "PySide6.scripts.pyside_tool:android_deploy"']
     _pyside_tools.remove("android_deploy")
 
-    _console_scripts.extend([f"pyside6-{tool} = PySide6.scripts.pyside_tool:{tool}"
+    _console_scripts.extend([f'pyside6-{tool} = "PySide6.scripts.pyside_tool:{tool}"'
                             for tool in _pyside_tools])
 
     setup = SetupData(
         name="PySide6_Essentials",
         version=get_version_from_package("PySide6", package_path),  # we use 'PySide6' here
-        description="Python bindings for the Qt cross-platform application and UI framework (Essentials)",
+        description=f"{PYSIDE_DESCRIPTION} (Essentials)",
         readme="README.pyside6_essentials.md",
         console_scripts=_console_scripts
     )
@@ -252,7 +262,7 @@ def wheel_pyside6_addons(package_path: Path) -> Tuple[SetupData, List[ModuleData
     setup = SetupData(
         name="PySide6_Addons",
         version=get_version_from_package("PySide6", package_path),  # we use 'PySide6' here
-        description="Python bindings for the Qt cross-platform application and UI framework (Addons)",
+        description=f"{PYSIDE_DESCRIPTION} (Addons)",
         readme="README.pyside6_addons.md",
         console_scripts=[],
     )
@@ -266,7 +276,7 @@ def wheel_pyside6(package_path: Path) -> Tuple[SetupData, Optional[List[ModuleDa
     setup = SetupData(
         name="PySide6",
         version=get_version_from_package("PySide6", package_path),
-        description="Python bindings for the Qt cross-platform application and UI framework",
+        description=PYSIDE_DESCRIPTION,
         readme="README.pyside6.md",
         console_scripts=[],
     )
@@ -326,6 +336,33 @@ def get_build_directory(options: Namespace):
     raise Exception("Unable to determine build directory, no matching virtual environment found")
 
 
+def check_modules_consistency():
+    available_functions = dir(build_scripts.wheel_files)
+    functions = [i.replace("module_", "") for i in available_functions if i.startswith("module_")]
+
+    sources = [i.stem for i in Path("sources/pyside6/PySide6/").glob("Qt*")]
+
+    missing_modules = set(sources) - set(functions)
+
+    if len(missing_modules):
+        print("Warning: the following modules don't have a function "
+              f"in 'build_scripts/wheel_files.py':\n  {missing_modules}")
+
+    # Check READMEs
+    readme_modules = set()
+    for r in Path(".").glob("README.pyside6*"):
+        with open(r) as f:
+            for line in f:
+                if line.startswith("* Qt"):
+                    readme_modules.add(line.strip().replace("* ", ""))
+
+    missing_modules_readme = set(sources) - readme_modules
+
+    if len(missing_modules_readme):
+        print("Warning: the following modules are not in READMEs :"
+              f"\n  {missing_modules_readme}")
+
+
 if __name__ == "__main__":
 
     parser = ArgumentParser()
@@ -344,6 +381,10 @@ if __name__ == "__main__":
     )
     options = parser.parse_args()
 
+    # Sanity check between the available modules,
+    # and the functions in build_scripts/wheel_files.py
+    check_modules_consistency()
+
     build_directory = get_build_directory(options)
 
     verbose = False
@@ -423,18 +464,18 @@ if __name__ == "__main__":
         # 5. call the build module to create the wheel
         print("-- Creating wheels")
         if not verbose:
-            _runner = build.pep517.wrappers.quiet_subprocess_runner
+            _runner = pyproject_hooks.quiet_subprocess_runner
         else:
-            _runner = build.pep517.wrappers.default_subprocess_runner
+            _runner = pyproject_hooks.default_subprocess_runner
         builder = build.ProjectBuilder(package_path, runner=_runner)
-        builder.build("wheel", "dist_new")
+        builder.build("wheel", "dist")
 
-        # 7. Copy wheels back
-        print("-- Copying wheels to dist_new/")
-        dist_path = Path("dist_new")
+        # 6. Copy wheels back
+        print("-- Copying wheels to dist/")
+        dist_path = Path("dist")
         if not dist_path.is_dir():
             dist_path.mkdir()
-        for wheel in Path(package_path / "dist_new").glob("*.whl"):
+        for wheel in Path(package_path / "dist").glob("*.whl"):
             copy(wheel, dist_path / wheel.name)
 
         # 8. Remove leftover files
index 263569264b10981125234cc6f8efe49339cd22dc..64058de7ae4376e704c69b2f42bad99d541f3f78 100644 (file)
@@ -21,8 +21,7 @@ information about a particular change.
    (PySide6_Examples).
  - [PYSIDE-748]  An optional parameter "tag" has been added to @Slot, allowing
                  to set QMetaMethod.tag().
- - [PYSIDE-2500] QMetaMethod.invoke() has been added. The invocation functions
-                 of QMetaObject now accept up to 10 arguments.
+ - [PYSIDE-769]  QtAsyncio: Support for ThreadPoolExecutor has been added.
  - [PYSIDE-841]  QtQuick3D procedural texture and QRhi window examples have
                  been added.
  - [PYSIDE-2230] Support for Python 3.12 has been added.
@@ -31,6 +30,8 @@ information about a particular change.
  - [PYSIDE-2485] A QLocale-related crash on macOS has been fixed.
  - [PYSIDE-2487] Error messages around signal/slot connections have been improved.
  - [PYSIDE-2494] The install location of QtAsyncio for CMake builds has been fixed.
+ - [PYSIDE-2500] QMetaMethod.invoke() has been added. The invocation functions
+                 of QMetaObject now accept up to 10 arguments.
  - [PYSIDE-2509] The signature of SignalInstance.connect() has been fixed.
  - [PYSIDE-2510] An error is now set when instantiating a Signal on a non-QObject.
  - [PYSIDE-2514] The type hints of QAbstractItemView.setModel()/QGraphicsView.setScene()
diff --git a/doc/changelogs/changes-6.6.2 b/doc/changelogs/changes-6.6.2
new file mode 100644 (file)
index 0000000..41ee1f1
--- /dev/null
@@ -0,0 +1,66 @@
+Qt for Python 6.6.2 is a bug-fix release.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qtforpython/
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+*                                  PySide6                                 *
+****************************************************************************
+
+ - [PYSIDE-535]  PyPySide has been updated to Python 3.10
+ - [PYSIDE-769]  QtAsyncio: call_soon_threadsafe() and QtAsyncio.run() (with
+   [PYSIDE-1112] keep_running and quit_qapp arguments) have been implemented.
+   [PYSIDE-2561] A bug with Python 3.12 has been fixed. Documentation has
+   [PYSIDE-2575] been added. Keyboard interrupts are now caught. Cancelling
+                 waiting tasks has been fixed. Tasks are now awaitable.
+ - [PYSIDE-1106] Documentation: The generation process has been optimized
+                 and warnings have been reduced.
+ - [PYSIDE-1586] Qt Charts: class QColorAxis has been added.
+ - [PYSIDE-1612] Android Deployment: The QtQuick dependency is now added
+                 when present.
+ - [PYSIDE-1612] Deployment problems when using pyenv have been fixed.
+ - [PYSIDE-1612] Nuitka has been upgraded to 1.8.0
+ - [PYSIDE-1612] Deployment: An icon for the application has been added.
+ - [PYSIDE-1612] A separate requirements.txt for Android deployment has been
+                 added.
+ - [PYSIDE-1612] Android Deployment: `buildozer` is now installed with the
+                 --init cli argument
+ - [PYSIDE-1931] Further hangs in QSql(Relational)TableModel.select() (PostGres
+                 with SSH tunnel) have been fixed.
+ - [PYSIDE-2206] Examples quick3d/proceduraltexture, sql/books have been
+                 updated. The contactslist has been added. Examples supported
+                 on Android are tagged as `Android`.
+ - [PYSIDE-2439] FindPython is now used instead of the deprecated
+                 FindPythonInterp and FindPythonLibs CMake modules.
+ - [PYSIDE-2539] Documentation: The enum name is now generated for flags.
+ - [PYSIDE-2544] A crash accessing QDBusVariant.variant() has been fixed.
+ - [PYSIDE-2547] A hang in QDBusConnection.connect() has been fixed.
+ - [PYSIDE-2558] Documentation: the differences between commercial and lts
+                 releases have been clarified.
+ - [PYSIDE-2568] A crash of pyside6-designer with pyenv on Unix has been
+                 fixed.
+ - [PYSIDE-2574] Documentation: A favicon for browser tabs has been added.
+
+****************************************************************************
+*                                  Shiboken6                               *
+****************************************************************************
+
+ - [PYSIDE-1106] Documentation: An option to disable the inheritance diagram
+                 has been added.
+ - [PYSIDE-1735] An error in the generated code when all enum values are
+                 deprecated has been fixed.
+ - [PYSIDE-2404] pyi file generation can now be disabled for debugging.
+ - [PYSIDE-2530] yocto cross builds have been fixed to find
+                 shiboken_wrapper.sh.
+ - [PYSIDE-2577] Documentation/doxygen: A bug querying const functions
+                 has been fixed.
index 0cb419817ffe6bd38daa0723171f0d4b78b3dda9..598d9b4bd1f5158716c9213ccfe12b384b8e5a55 100644 (file)
@@ -5,10 +5,9 @@ from PySide6.QtCore import (Qt, QObject, Signal, Slot)
 from PySide6.QtGui import (QColor, QFont, QPalette)
 from PySide6.QtWidgets import (QApplication, QGridLayout, QLabel, QMainWindow, QVBoxLayout, QWidget)
 
-from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
+import PySide6.QtAsyncio as QtAsyncio
 
 import asyncio
-import signal
 import sys
 from random import randint
 
@@ -132,8 +131,4 @@ if __name__ == "__main__":
 
     main_window.show()
 
-    signal.signal(signal.SIGINT, signal.SIG_DFL)
-
-    asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
-    asyncio.ensure_future(eratosthenes.start())
-    asyncio.get_event_loop().run_forever()
+    QtAsyncio.run(eratosthenes.start())
index a66e07ef61e41efa075d8ca62ac3966330204089..4545f35d51e710102e98eb55addc11230d7b258a 100644 (file)
@@ -4,10 +4,9 @@
 from PySide6.QtCore import (Qt, QObject, Signal, Slot)
 from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget)
 
-from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
+import PySide6.QtAsyncio as QtAsyncio
 
 import asyncio
-import signal
 import sys
 
 
@@ -60,7 +59,4 @@ if __name__ == "__main__":
 
     main_window.show()
 
-    signal.signal(signal.SIGINT, signal.SIG_DFL)
-
-    asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
-    asyncio.get_event_loop().run_forever()
+    QtAsyncio.run()
index abc0599e5af4a424bd2c2562c10c5526a92e9ce3..bcd4c27148d261b683896f8f6ecc49ef7bbbeaf7 100644 (file)
@@ -7,7 +7,7 @@ import sys
 from PySide6.QtAxContainer import QAxSelect, QAxWidget
 from PySide6.QtGui import QAction
 from PySide6.QtWidgets import (QApplication, QDialog,
-    QMainWindow, QMessageBox, QToolBar)
+                               QMainWindow, QMessageBox, QToolBar)
 
 
 class MainWindow(QMainWindow):
@@ -25,7 +25,7 @@ class MainWindow(QMainWindow):
         fileMenu.addAction(exitAction)
 
         aboutMenu = self.menuBar().addMenu("&About")
-        aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
+        aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)  # noqa: F821
         aboutMenu.addAction(aboutQtAct)
         self.axWidget = QAxWidget()
         self.setCentralWidget(self.axWidget)
index e51160b849603dd60e59d5a18945c74846164ca2..c75f5b8a1df078f044feca0cbf314d0ce018e006 100644 (file)
@@ -46,7 +46,7 @@ class DeviceDiscoveryDialog(QDialog):
             item = QListWidgetItem(label)
             pairing_status = self._local_device.pairingStatus(info.address())
             if (pairing_status == QBluetoothLocalDevice.Paired
-                or pairing_status == QBluetoothLocalDevice.AuthorizedPaired):
+                    or pairing_status == QBluetoothLocalDevice.AuthorizedPaired):
                 item.setForeground(QColor(Qt.green))
             else:
                 item.setForeground(QColor(Qt.black))
@@ -123,7 +123,8 @@ class DeviceDiscoveryDialog(QDialog):
         items = self._ui.list.findItems(address.toString(), Qt.MatchContains)
 
         color = QColor(Qt.red)
-        if pairing == QBluetoothLocalDevice.Paired or pairing == QBluetoothLocalDevice.AuthorizedPaired:
+        if (pairing == QBluetoothLocalDevice.Paired
+                or pairing == QBluetoothLocalDevice.AuthorizedPaired):
             color = QColor(Qt.green)
         for item in items:
             item.setForeground(color)
index 79f2720e75b34104e84ca790255e9dd5a5585308..421446c8e8dd47e607b523cfd37c731efabcc00b 100644 (file)
@@ -5,7 +5,7 @@ import sys
 
 from PySide6.QtBluetooth import QBluetoothLocalDevice
 from PySide6.QtQml import QmlElement
-from PySide6.QtCore import QObject, Property, Signal, Slot, Qt, QCoreApplication
+from PySide6.QtCore import QObject, Property, Signal, Slot, Qt
 
 from heartrate_global import simulator, is_android
 
@@ -61,9 +61,9 @@ class ConnectionHandler(QObject):
         if is_android:
             permission = QBluetoothPermission()
             permission.setCommunicationModes(QBluetoothPermission.Access)
-            permission_status = qApp.checkPermission(permission)
+            permission_status = qApp.checkPermission(permission)  # noqa: F821
             if permission_status == Qt.PermissionStatus.Undetermined:
-                qApp.requestPermission(permission, self, self.initLocalDevice)
+                qApp.requestPermission(permission, self, self.initLocalDevice)  # noqa: F821
                 return
             if permission_status == Qt.PermissionStatus.Denied:
                 return
index db42c2fa4bbcb57f2dfae3381ccb8497777403ec..5cabd1b5bc108d42d6039eac900c8921dd802f29 100644 (file)
@@ -4,7 +4,7 @@
 from PySide6.QtBluetooth import (QBluetoothDeviceDiscoveryAgent,
                                  QBluetoothDeviceInfo)
 from PySide6.QtQml import QmlElement
-from PySide6.QtCore import QTimer, Property, Signal, Slot, Qt, QCoreApplication
+from PySide6.QtCore import QTimer, Property, Signal, Slot, Qt
 
 from bluetoothbaseclass import BluetoothBaseClass
 from deviceinfo import DeviceInfo
@@ -49,9 +49,9 @@ class DeviceFinder(BluetoothBaseClass):
         if is_android:
             permission = QBluetoothPermission()
             permission.setCommunicationModes(QBluetoothPermission.Access)
-            permission_status = qApp.checkPermission(permission)
+            permission_status = qApp.checkPermission(permission)  # noqa: F821
             if permission_status == Qt.PermissionStatus.Undetermined:
-                qApp.requestPermission(permission, self, self.startSearch)
+                qApp.requestPermission(permission, self, self.startSearch)  # noqa: F82 1
                 return
             elif permission_status == Qt.PermissionStatus.Denied:
                 return
index 85d160c4def494b3d0d4eda691e72a33f9a7ff11..df34052b8ffa9555be3b9b848f697e11d11be19a 100644 (file)
@@ -105,7 +105,7 @@ class DeviceHandler(BluetoothBaseClass):
         # Disconnect and delete old connection
         if self.m_control:
             self.m_control.disconnectFromDevice()
-            m_control = None
+            self.m_control = None
 
         # Create new controller and connect it if device available
         if self.m_currentDevice:
@@ -166,7 +166,8 @@ class DeviceHandler(BluetoothBaseClass):
 #! [Filter HeartRate service 2]
         # If heartRateService found, create new service
         if self.m_foundHeartRateService:
-            self.m_service = self.m_control.createServiceObject(QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate), self)
+            self.m_service = self.m_control.createServiceObject(
+                QBluetoothUuid(QBluetoothUuid.ServiceClassUuid.HeartRate), self)
 
         if self.m_service:
             self.m_service.stateChanged.connect(self.serviceStateChanged)
@@ -185,9 +186,11 @@ class DeviceHandler(BluetoothBaseClass):
             self.info = "Discovering services..."
         elif switch == QLowEnergyService.RemoteServiceDiscovered:
             self.info = "Service discovered."
-            hrChar = self.m_service.characteristic(QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement))
+            hrChar = self.m_service.characteristic(
+                QBluetoothUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement))
             if hrChar.isValid():
-                self.m_notificationDesc = hrChar.descriptor(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration)
+                self.m_notificationDesc = hrChar.descriptor(
+                    QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration)
                 if self.m_notificationDesc.isValid():
                     self.m_service.writeDescriptor(self.m_notificationDesc,
                                                    QByteArray.fromHex(b"0100"))
@@ -233,7 +236,7 @@ class DeviceHandler(BluetoothBaseClass):
     @Slot(QLowEnergyCharacteristic, QByteArray)
     def confirmedDescriptorWrite(self, d, value):
         if (d.isValid() and d == self.m_notificationDesc
-            and value == QByteArray.fromHex(b"0000")):
+                and value == QByteArray.fromHex(b"0000")):
             # disabled notifications . assume disconnect intent
             self.m_control.disconnectFromDevice()
             self.m_service = None
@@ -244,7 +247,7 @@ class DeviceHandler(BluetoothBaseClass):
 
         # disable notifications
         if (self.m_notificationDesc.isValid() and self.m_service
-            and self.m_notificationDesc.value() == QByteArray.fromHex(b"0100")):
+                and self.m_notificationDesc.value() == QByteArray.fromHex(b"0100")):
             self.m_service.writeDescriptor(self.m_notificationDesc,
                                            QByteArray.fromHex(b"0000"))
         else:
@@ -301,6 +304,6 @@ class DeviceHandler(BluetoothBaseClass):
             self.m_sum += value
             self.m_avg = float(self.m_sum) / len(self.m_measurements)
             self.m_calories = ((-55.0969 + (0.6309 * self.m_avg) + (0.1988 * 94)
-                               + (0.2017 * 24)) / 4.184) * 60 * self.time / 3600
+                                + (0.2017 * 24)) / 4.184) * 60 * self.time / 3600
 
         self.statsChanged.emit()
index 0a0938caddf1544530d34c0fe132b29ebdd67c14..9d190d991848e7afcf8d917ea5cb7d2c8e706d55 100644 (file)
@@ -1,6 +1,8 @@
 Bluetooth Low Energy Heart Rate Game
 ====================================
 
+.. tags:: Android
+
 The Bluetooth Low Energy Heart Rate Game shows how to develop a
 Bluetooth Low Energy application using the Qt Bluetooth API. The
 application covers the scanning for Bluetooth Low Energy devices,
index dec6f83a3f88204864729eee65e49859210810d1..584c44d21cf013d36ba9584d5b67d7a63156ccf3 100644 (file)
@@ -1,7 +1,6 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 import os
-import sys
 
 
 _simulator = False
@@ -16,4 +15,5 @@ def set_simulator(s):
     global _simulator
     _simulator = s
 
+
 is_android = os.environ.get('ANDROID_ARGUMENT')
index f98cc6fe898ee7dba0c50883b26583bd77ba5c94..abbf4eb7f80b2a0e9f9de230b13740df001ee0bc 100644 (file)
@@ -39,8 +39,8 @@ if __name__ == '__main__':
     char_data.setUuid(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
     char_data.setValue(QByteArray(2, 0))
     char_data.setProperties(QLowEnergyCharacteristic.Notify)
-    client_config = QLowEnergyDescriptorData(QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration,
-                                             QByteArray(2, 0))
+    client_config = QLowEnergyDescriptorData(
+        QBluetoothUuid.DescriptorType.ClientCharacteristicConfiguration, QByteArray(2, 0))
     char_data.addDescriptor(client_config)
 
     service_data = QLowEnergyServiceData()
@@ -66,8 +66,9 @@ if __name__ == '__main__':
         value = QByteArray()
         value.append(chr(0))  # Flags that specify the format of the value.
         value.append(chr(current_heart_rate))  # Actual value.
-        characteristic = service.characteristic(QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
-        assert(characteristic.isValid())
+        characteristic = service.characteristic(
+            QBluetoothUuid.CharacteristicType.HeartRateMeasurement)
+        assert characteristic.isValid()
         # Potentially causes notification.
         service.writeCharacteristic(characteristic, value)
         if current_heart_rate == 60:
index a0e9df77e9056cc8f1dad74a52db7f42468190ea..42bde8753d01dfa310ba96892355b905bd2b877d 100644 (file)
@@ -85,4 +85,3 @@ class CharacteristicInfo(QObject):
     def characteristic(self, characteristic):
         self._characteristic = characteristic
         self.characteristic_changed.emit()
-
index e69a8450e2726065ce8e470e2936d21f9d8b99ea..09108cf69cd5bf1015296560a42bfd6f551de61c 100644 (file)
@@ -13,6 +13,7 @@ from characteristicinfo import CharacteristicInfo
 QML_IMPORT_NAME = "Scanner"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 @QmlSingleton
 class Device(QObject):
@@ -275,5 +276,3 @@ class Device(QObject):
     def stop_device_discovery(self):
         if self.discovery_agent.isActive():
             self.discovery_agent.stop()
-
-
index edcbef89deb3f92d6c51ac05b221b000df58dbcd..35a568821ea16e58c964c364daf7ef46068fe8b6 100644 (file)
@@ -32,4 +32,3 @@ class DeviceInfo(QObject):
     def set_device(self, device):
         self._device = device
         self.device_changed.emit()
-
index c20c2d8b7f626ef420edd73beaa672dd23b2e7e1..a0c574350beff1f9bf1160674df3413c425589d6 100644 (file)
@@ -1,6 +1,8 @@
 Bluetooth Low Energy Scanner Example
 ====================================
 
+.. tags:: Android
+
 A Python application that demonstrates the analogous example in Qt
 `Bluetooth Low Energy Scanner <https://doc.qt.io/qt-6/qtbluetooth-lowenergyscanner-example.html>`_
 
index dfbff97b3e5c55f6d34e6b6fc65510e4396322a5..ec12f99e73d9e385634753237f40f3a6a250da62 100644 (file)
@@ -10,7 +10,7 @@ from PySide6.QtCore import QCoreApplication
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine
 
-from device import Device
+from device import Device  # noqa: F401
 from pathlib import Path
 
 if __name__ == '__main__':
index 092e9898f090e7cdb7a441aec122d2e5a0d4987b..cddffe6636196e9ccd3d6a3d339405f74039be4f 100644 (file)
@@ -62,5 +62,3 @@ class ServiceInfo(QObject):
     @service.setter
     def service(self, service):
         self._service = service
-
-
index 3de00b8df91e414f75848cae36e140357004c326..622938d1664ebfc3678d5114a06ecca293527811 100644 (file)
@@ -4,8 +4,8 @@
 """PySide6 port of the Callout example from Qt v5.x"""
 
 import sys
-from PySide6.QtWidgets import (QApplication, QGraphicsScene,
-    QGraphicsView, QGraphicsSimpleTextItem, QGraphicsItem)
+from PySide6.QtWidgets import (QApplication, QGraphicsScene, QGraphicsView,
+                               QGraphicsSimpleTextItem, QGraphicsItem)
 from PySide6.QtCore import Qt, QPointF, QRectF, QRect
 from PySide6.QtCharts import QChart, QLineSeries, QSplineSeries
 from PySide6.QtGui import QPainter, QFont, QFontMetrics, QPainterPath, QColor
@@ -42,37 +42,37 @@ class Callout(QGraphicsItem):
 
             # establish the position of the anchor point in relation to _rect
             above = anchor.y() <= self._rect.top()
-            above_center = (anchor.y() > self._rect.top() and
-                anchor.y() <= self._rect.center().y())
-            below_center = (anchor.y() > self._rect.center().y() and
-                anchor.y() <= self._rect.bottom())
+            above_center = (anchor.y() > self._rect.top()
+                            and anchor.y() <= self._rect.center().y())
+            below_center = (anchor.y() > self._rect.center().y()
+                            and anchor.y() <= self._rect.bottom())
             below = anchor.y() > self._rect.bottom()
 
             on_left = anchor.x() <= self._rect.left()
-            left_of_center = (anchor.x() > self._rect.left() and
-                anchor.x() <= self._rect.center().x())
-            right_of_center = (anchor.x() > self._rect.center().x() and
-                anchor.x() <= self._rect.right())
+            left_of_center = (anchor.x() > self._rect.left()
+                              and anchor.x() <= self._rect.center().x())
+            right_of_center = (anchor.x() > self._rect.center().x()
+                               and anchor.x() <= self._rect.right())
             on_right = anchor.x() > self._rect.right()
 
             # get the nearest _rect corner.
             x = (on_right + right_of_center) * self._rect.width()
             y = (below + below_center) * self._rect.height()
-            corner_case = ((above and on_left) or (above and on_right) or
-                (below and on_left) or (below and on_right))
+            corner_case = ((above and on_left) or (above and on_right)
+                           or (below and on_left) or (below and on_right))
             vertical = abs(anchor.x() - x) > abs(anchor.y() - y)
 
-            x1 = (x + left_of_center * 10 - right_of_center * 20 + corner_case *
-                int(not vertical) * (on_left * 10 - on_right * 20))
-            y1 = (y + above_center * 10 - below_center * 20 + corner_case *
-                vertical * (above * 10 - below * 20))
+            x1 = (x + left_of_center * 10 - right_of_center * 20 + corner_case
+                  * int(not vertical) * (on_left * 10 - on_right * 20))
+            y1 = (y + above_center * 10 - below_center * 20 + corner_case
+                  * vertical * (above * 10 - below * 20))
             point1.setX(x1)
             point1.setY(y1)
 
-            x2 = (x + left_of_center * 20 - right_of_center * 10 + corner_case *
-                int(not vertical) * (on_left * 20 - on_right * 10))
-            y2 = (y + above_center * 20 - below_center * 10 + corner_case *
-                vertical * (above * 20 - below * 10))
+            x2 = (x + left_of_center * 20 - right_of_center * 10 + corner_case
+                  * int(not vertical) * (on_left * 20 - on_right * 10))
+            y2 = (y + above_center * 20 - below_center * 10 + corner_case
+                  * vertical * (above * 20 - below * 10))
             point2.setX(x2)
             point2.setY(y2)
 
@@ -90,7 +90,7 @@ class Callout(QGraphicsItem):
 
     def mouseMoveEvent(self, event):
         if event.buttons() & Qt.LeftButton:
-            self.setPos(mapToParent(
+            self.setPos(self.mapToParent(
                 event.pos() - event.buttonDownPos(Qt.LeftButton)))
             event.setAccepted(True)
         else:
@@ -127,7 +127,7 @@ class View(QGraphicsView):
         self._chart = QChart()
         self._chart.setMinimumSize(640, 480)
         self._chart.setTitle("Hover the line to show callout. Click the line "
-            "to make it stay")
+                             "to make it stay")
         self._chart.legend().hide()
         self.series = QLineSeries()
         self.series.append(1, 3)
index 28c819c16e0b00be264ccff928e34f9c6fecb19e..5787710ca236de15e67672e70fc67d138c60368f 100644 (file)
@@ -7,7 +7,7 @@ import sys
 from PySide6.QtCore import QPointF, Qt
 from PySide6.QtGui import QColor, QPainter, QPalette
 from PySide6.QtWidgets import (QApplication, QMainWindow, QSizePolicy,
-    QWidget)
+                               QWidget)
 from PySide6.QtCharts import (QAreaSeries, QBarSet, QChart, QChartView,
                               QLineSeries, QPieSeries, QScatterSeries,
                               QSplineSeries, QStackedBarSeries)
@@ -27,7 +27,7 @@ class ThemeWidget(QWidget):
         self.value_max = 10
         self.value_count = 7
         self.data_table = self.generate_random_data(self.list_count,
-            self.value_max, self.value_count)
+                                                    self.value_max, self.value_count)
 
         self.ui.setupUi(self)
         self.populate_themebox()
@@ -41,8 +41,7 @@ class ThemeWidget(QWidget):
 
         # Pie Chart
         chart_view = QChartView(self.create_pie_chart())
-        chart_view.setSizePolicy(QSizePolicy.Ignored,
-            QSizePolicy.Ignored)
+        chart_view.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
         self.ui.gridLayout.addWidget(chart_view, 1, 1)
         self.charts.append(chart_view)
 
@@ -70,10 +69,10 @@ class ThemeWidget(QWidget):
         self.ui.antialiasCheckBox.setChecked(True)
 
         # Set the colors from the light theme as default ones
-        pal = qApp.palette()
+        pal = qApp.palette()  # noqa: F821
         pal.setColor(QPalette.Window, QColor(0xf0f0f0))
         pal.setColor(QPalette.WindowText, QColor(0x404044))
-        qApp.setPalette(pal)
+        qApp.setPalette(pal)  # noqa: F821
 
         self.update_ui()
 
@@ -273,7 +272,6 @@ class ThemeWidget(QWidget):
                 for chart_view in self.charts:
                     chart_view.chart().setTheme(theme)
 
-
                 # Set palette colors based on selected theme
                 if theme == QChart.ChartThemeLight:
                     set_colors(QColor(0xf0f0f0), QColor(0x404044))
index 1597685c2f126c4b43febd911f84c80da7907ecf..9111d4aacc843cc29bf0a46af4685c0f41dd4e70 100644 (file)
@@ -38,7 +38,7 @@ class MainSlice(QPieSlice):
 class DonutBreakdownChart(QChart):
     def __init__(self, parent=None):
         super().__init__(QChart.ChartTypeCartesian,
-                                                  parent, Qt.WindowFlags())
+                         parent, Qt.WindowFlags())
         self.main_series = QPieSeries()
         self.main_series.setPieSize(0.7)
         self.addSeries(self.main_series)
index f1929547dc6274d7ca80d53d4997021b6a95b88d..5417a940f6e254af27972a7b0691bf681467290e 100644 (file)
@@ -7,7 +7,8 @@ import sys
 from PySide6.QtCore import Qt, QRectF, Slot
 from PySide6.QtGui import QBrush, QColor, QPainter, QPen
 from PySide6.QtWidgets import (QApplication, QDoubleSpinBox,
-    QFormLayout, QGridLayout, QGroupBox, QPushButton, QWidget)
+                               QFormLayout, QGridLayout, QGroupBox,
+                               QPushButton, QWidget)
 from PySide6.QtCharts import QBarSeries, QBarSet, QChart, QChartView
 
 
@@ -206,10 +207,8 @@ class MainWidget(QWidget):
     def update_legend_layout(self):
         legend = self.chart.legend()
 
-        rect = QRectF(self.legend_posx.value(),
-            self.legend_posy.value(),
-            self.legend_width.value(),
-            self.legend_height.value())
+        rect = QRectF(self.legend_posx.value(), self.legend_posy.value(),
+                      self.legend_width.value(), self.legend_height.value())
         legend.setGeometry(rect)
 
         legend.update()
index d0bf842e542e4804954a196dac97d3c566b87d77..3ba42368ef05aec6aa34e1347fdff28658a8a868 100644 (file)
@@ -54,7 +54,7 @@ def get_memory_usage():
                     legend = f'{command} {memory_usage}%'
                     result.append([legend, memory_usage])
 
-    result.sort(key = lambda x: x[1], reverse=True)
+    result.sort(key=lambda x: x[1], reverse=True)
     return result
 
 
index 6460210372eb802fbbb6b1f2e3f00833bafb4b89..0e36f777032786c1571e944a491b1e33c69bf68f 100644 (file)
@@ -9,7 +9,7 @@ from random import randrange
 from PySide6.QtCore import QAbstractTableModel, QModelIndex, QRect, Qt
 from PySide6.QtGui import QColor, QPainter
 from PySide6.QtWidgets import (QApplication, QGridLayout, QHeaderView,
-    QTableView, QWidget)
+                               QTableView, QWidget)
 from PySide6.QtCharts import QChart, QChartView, QLineSeries, QVXYModelMapper
 
 
@@ -101,9 +101,6 @@ class TableWidget(QWidget):
         self.mapper.setModel(self.model)
         self.chart.addSeries(self.series)
 
-        # for storing color hex from the series
-        seriesColorHex = "#000000"
-
         # get the color of the series and use it for showing the mapped area
         self.model.add_mapping(self.series.pen().color().name(),
                                QRect(0, 0, 2, self.model.rowCount()))
index cfb11800ad1ec00248cdfb65b974dff1b06d475b..9f70c03285864aa656289bf1a54dd2c116ed0048 100644 (file)
@@ -21,11 +21,11 @@ class MainWindow(QMainWindow):
         set3 = QBarSet("Mary")
         set4 = QBarSet("Samantha")
 
-        set0.append([1, 2, 3,  4, 5, 6])
-        set1.append([5, 0, 0,  4, 0, 7])
+        set0.append([1, 2, 3, 4, 5, 6])
+        set1.append([5, 0, 0, 4, 0, 7])
         set2.append([3, 5, 8, 13, 8, 5])
-        set3.append([5, 6, 7,  3, 4, 5])
-        set4.append([9, 7, 5,  3, 1, 2])
+        set3.append([5, 6, 7, 3, 4, 5])
+        set4.append([9, 7, 5, 3, 1, 2])
 
         series = QPercentBarSeries()
         series.append(set0)
index 055802b9f7179e8a1b59e0e0c14a13f813066007..36b10aa1633de52400f02c3c159784043d41d5be 100644 (file)
@@ -109,14 +109,14 @@ class ChartWindow(QMainWindow):
         self._selectedPointConfig = self._series.pointConfiguration(index)
         selected_point = self._series.at(index)
         selected_index_lineedit = self._selected_point_index_lineedit
-        selected_index_lineedit.setText("(" + str(selected_point.x()) + ", " +
-                                        str(selected_point.y()) + ")")
+        selected_index_lineedit.setText("(" + str(selected_point.x()) + ", "
+                                        str(selected_point.y()) + ")")
         config = self._series.pointConfiguration(index)
 
         color = config.get(PointConfig.Color) or self._series.color()
         size = config.get(PointConfig.Size) or self._series.markerSize()
-        labelVisibility = (config.get(PointConfig.LabelVisibility) or
-                           self._series.pointLabelsVisible())
+        labelVisibility = (config.get(PointConfig.LabelVisibility)
+                           or self._series.pointLabelsVisible())
         customLabel = config.get(PointConfig.LabelFormat) or ""
 
         combobox_value_list = [
index 4f9540d429857829c21c24e4ee5fef0a103a75f3..df7b61687f3ae6b7bee8d0ebc46a73344958e381 100644 (file)
@@ -7,7 +7,8 @@ import sys
 from PySide6.QtCore import Slot, QPointF, Qt
 from PySide6.QtCharts import QChart, QChartView, QSplineSeries
 from PySide6.QtGui import QPainter, QImage
-from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QGridLayout, QComboBox, QCheckBox, QLabel, QHBoxLayout
+from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout,
+                               QComboBox, QCheckBox, QLabel, QHBoxLayout)
 
 import utilities as Utilities
 
@@ -20,12 +21,12 @@ if __name__ == "__main__":
     marker_size = 20.
     series = QSplineSeries()
     series.append([QPointF(0, 0),
-                    QPointF(0.5, 2.27),
-                    QPointF(1.5, 2.2),
-                    QPointF(3.3, 1.7),
-                    QPointF(4.23, 3.1),
-                    QPointF(5.3, 2.3),
-                    QPointF(6.47, 4.1)])
+                   QPointF(0.5, 2.27),
+                   QPointF(1.5, 2.2),
+                   QPointF(3.3, 1.7),
+                   QPointF(4.23, 3.1),
+                   QPointF(5.3, 2.3),
+                   QPointF(6.47, 4.1)])
     series.setMarkerSize(marker_size)
     series.setLightMarker(Utilities.default_light_marker(marker_size))
     series.setSelectedLightMarker(Utilities.default_selected_light_marker(marker_size))
@@ -66,16 +67,16 @@ if __name__ == "__main__":
     char_point_combobox.addItems(["Red rectangle", "Green triangle", "Orange circle"])
     char_point_combobox.currentIndexChanged.connect(set_light_marker)
 
-
     @Slot(int)
     def set_selected_light_marker(index):
-        series.setSelectedLightMarker(Utilities.get_selected_point_representation(Utilities.selected_point_type(index), marker_size))
+        series.setSelectedLightMarker(
+            Utilities.get_selected_point_representation(
+                Utilities.selected_point_type(index), marker_size))
 
     char_point_selected = QLabel("Char point selected: ")
     char_point_selected_combobox.addItems(["Blue triangle", "Yellow rectangle", "Lavender circle"])
     char_point_selected_combobox.currentIndexChanged.connect(set_selected_light_marker)
 
-
     @Slot(int)
     def set_line_color(index):
         series.setColor(Utilities.make_line_color(Utilities.line_color(index)))
@@ -84,11 +85,12 @@ if __name__ == "__main__":
     line_color_combobox.addItems(["Blue", "Black", "Mint"])
     line_color_combobox.currentIndexChanged.connect(set_line_color)
 
-
     @Slot(int)
     def display_unselected_points(checkbox_state):
         if checkbox_state:
-            series.setLightMarker(Utilities.get_point_representation(Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
+            series.setLightMarker(
+                Utilities.get_point_representation(
+                    Utilities.point_type(char_point_combobox.currentIndex()), marker_size))
         else:
             series.setLightMarker(QImage())
 
@@ -96,7 +98,6 @@ if __name__ == "__main__":
     show_unselected_points_checkbox.setChecked(True)
     show_unselected_points_checkbox.stateChanged.connect(display_unselected_points)
 
-
     control_label = QLabel("Marker and Selection Controls")
     control_label.setAlignment(Qt.AlignHCenter)
     control_label_font = control_label.font()
index 6b96d6e26b6c15d62bdf1f3d33722bb5e92c6d4c..b27a2542b7e9c077e723d9d11029fdebef04ebae 100644 (file)
@@ -4,7 +4,8 @@
 from PySide6.QtGui import QImage, QPainter, QColor
 from PySide6.QtCore import Qt
 
-import rc_markers
+import rc_markers  # noqa: F401
+
 
 def rectangle(point_type, image_size):
     image = QImage(image_size, image_size, QImage.Format_RGB32)
@@ -15,9 +16,11 @@ def rectangle(point_type, image_size):
     painter.end()
     return image
 
+
 def triangle(point_type, image_size):
     return QImage(point_type[3]).scaled(image_size, image_size)
 
+
 def circle(point_type, image_size):
     image = QImage(image_size, image_size, QImage.Format_ARGB32)
     image.fill(QColor(0, 0, 0, 0))
@@ -32,6 +35,7 @@ def circle(point_type, image_size):
     painter.end()
     return image
 
+
 _point_types = [("RedRectangle", rectangle, Qt.red),
                 ("GreenTriangle", triangle, Qt.green, ":/images/green_triangle.png"),
                 ("OrangeCircle", circle, QColor(255, 127, 80))]
@@ -40,12 +44,15 @@ _selected_point_types = [("BlueTriangle", triangle, Qt.blue, ":/images/blue_tria
                          ("LavenderCircle", circle, QColor(147, 112, 219))]
 _line_colors = [("Blue", QColor(65, 105, 225)), ("Black", Qt.black), ("Mint", QColor(70, 203, 155))]
 
+
 def point_type(index):
     return _point_types[index]
 
+
 def selected_point_type(index):
     return _selected_point_types[index]
 
+
 def line_color(index):
     return _line_colors[index]
 
@@ -53,6 +60,7 @@ def line_color(index):
 def default_light_marker(image_size):
     return rectangle(_point_types[0], image_size)
 
+
 def default_selected_light_marker(image_size):
     return triangle(_selected_point_types[0], image_size)
 
@@ -60,8 +68,10 @@ def default_selected_light_marker(image_size):
 def get_point_representation(point_type, image_size):
     return point_type[1](point_type, image_size)
 
+
 def get_selected_point_representation(point_type, image_size):
     return point_type[1](point_type, image_size)
 
+
 def make_line_color(line_color):
     return line_color[1]
index c374f01197b56cc2c8b35c2eb58d7a6a6191946a..1349000476e71ea4e1aa1c2f81e17d4924ac44ad 100644 (file)
@@ -90,4 +90,4 @@ class Dialog(QDialog):
 
     def detach(self):
         if not self._shared_memory.detach():
-            self.ui.label.setText(tr("Unable to detach from shared memory."))
+            self.ui.label.setText(self.tr("Unable to detach from shared memory."))  # noqa: F821
index a77349e5e2f9a03e0e3b099eb48d15b910ae4d2b..f87a2f4b572cb427a6afdfa70b6cd3f662c546ea 100644 (file)
@@ -7,14 +7,20 @@
 import sys
 
 from PySide6.QtCore import (QByteArray, QDate, QDateTime, QDir, QEvent, QPoint,
-    QRect, QRegularExpression, QSettings, QSize, QTime, QTimer, Qt, Slot)
+                            QRect, QRegularExpression, QSettings, QSize, QTime,
+                            QTimer, Qt, Slot)
 from PySide6.QtGui import (QAction, QColor, QIcon, QIntValidator,
-    QDoubleValidator, QRegularExpressionValidator, QValidator)
+                           QDoubleValidator, QRegularExpressionValidator,
+                           QValidator)
 from PySide6.QtWidgets import (QAbstractItemView, QApplication,
-    QCheckBox, QComboBox, QFileDialog, QDialog, QDialogButtonBox, QGridLayout,
-    QGroupBox, QHeaderView, QInputDialog, QItemDelegate, QLabel, QLineEdit,
-    QMainWindow, QMessageBox, QStyle, QSpinBox, QStyleOptionViewItem,
-    QTableWidget, QTableWidgetItem, QTreeWidget, QTreeWidgetItem, QVBoxLayout)
+                               QCheckBox, QComboBox, QFileDialog, QDialog,
+                               QDialogButtonBox, QGridLayout,
+                               QGroupBox, QHeaderView, QInputDialog,
+                               QItemDelegate, QLabel, QLineEdit,
+                               QMainWindow, QMessageBox, QStyle, QSpinBox,
+                               QStyleOptionViewItem, QTableWidget,
+                               QTableWidgetItem, QTreeWidget, QTreeWidgetItem,
+                               QVBoxLayout)
 
 
 class TypeChecker:
@@ -160,7 +166,7 @@ class MainWindow(QMainWindow):
     @Slot()
     def open_inifile(self):
         file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File",
-                '', "INI Files (*.ini *.conf)")
+                                                   '', "INI Files (*.ini *.conf)")
 
         if file_name:
             self.load_ini_file(file_name)
@@ -175,7 +181,8 @@ class MainWindow(QMainWindow):
     @Slot()
     def open_property_list(self):
         file_name, _ = QFileDialog.getOpenFileName(self,
-                "Open Property List", '', "Property List Files (*.plist)")
+                                                   "Open Property List", '',
+                                                   "Property List Files (*.plist)")
 
         if file_name:
             settings = QSettings(file_name, QSettings.NativeFormat)
@@ -185,8 +192,8 @@ class MainWindow(QMainWindow):
     @Slot()
     def open_registry_path(self):
         path, ok = QInputDialog.getText(self, "Open Registry Path",
-                "Enter the path in the Windows registry:",
-                QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
+                                        "Enter the path in the Windows registry:",
+                                        QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
 
         if ok and path != '':
             settings = QSettings(path, QSettings.NativeFormat)
@@ -196,56 +203,46 @@ class MainWindow(QMainWindow):
     @Slot()
     def about(self):
         QMessageBox.about(self, "About Settings Editor",
-                "The <b>Settings Editor</b> example shows how to access "
-                "application settings using Qt.")
-
-    def create_actions(self):
-        self._open_settings_act = QAction("&Open Application Settings...",
-                self, shortcut="Ctrl+O", triggered=self.openSettings)
-
-        self._open_ini_file_act = QAction("Open I&NI File...", self,
-                shortcut="Ctrl+N", triggered=self.openIniFile)
-
-        self._open_property_list_act = QAction("Open macOS &Property List...",
-                self, shortcut="Ctrl+P", triggered=self.openPropertyList)
+                          "The <b>Settings Editor</b> example shows how to access "
+                          "application settings using Qt.")
 
     def create_actions(self):
         self.open_settings_action = QAction("&Open Application Settings...",
-                self, shortcut="Ctrl+O", triggered=self.open_settings)
+                                            self, shortcut="Ctrl+O", triggered=self.open_settings)
 
         self.open_ini_file_action = QAction("Open I&NI File...", self,
-                shortcut="Ctrl+N", triggered=self.open_inifile)
+                                            shortcut="Ctrl+N", triggered=self.open_inifile)
 
-        self.open_property_list_action = QAction("Open macOS &Property List...",
-                self, shortcut="Ctrl+P", triggered=self.open_property_list)
+        self.open_property_list_action = QAction("Open macOS &Property List...", self,
+                                                 shortcut="Ctrl+P",
+                                                 triggered=self.open_property_list)
         if sys.platform != 'darwin':
             self.open_property_list_action.setEnabled(False)
 
         self.open_registry_path_action = QAction(
-                "Open Windows &Registry Path...", self, shortcut="Ctrl+G",
-                triggered=self.open_registry_path)
+            "Open Windows &Registry Path...", self, shortcut="Ctrl+G",
+            triggered=self.open_registry_path)
         if sys.platform != 'win32':
             self.open_registry_path_action.setEnabled(False)
 
         self.refresh_action = QAction("&Refresh", self, shortcut="Ctrl+R",
-                enabled=False, triggered=self.settings_tree.refresh)
+                                      enabled=False, triggered=self.settings_tree.refresh)
 
-        self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q",
-                triggered=self.close)
+        self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close)
 
         self.auto_refresh_action = QAction("&Auto-Refresh", self,
-                shortcut="Ctrl+A", checkable=True, enabled=False)
+                                           shortcut="Ctrl+A", checkable=True, enabled=False)
         self.auto_refresh_action.triggered[bool].connect(self.settings_tree.set_auto_refresh)
         self.auto_refresh_action.triggered[bool].connect(self.refresh_action.setDisabled)
 
         self.fallbacks_action = QAction("&Fallbacks", self,
-                shortcut="Ctrl+F", checkable=True, enabled=False)
+                                        shortcut="Ctrl+F", checkable=True, enabled=False)
         self.fallbacks_action.triggered[bool].connect(self.settings_tree.set_fallbacks_enabled)
 
         self.about_action = QAction("&About", self, triggered=self.about)
 
         self.about_Qt_action = QAction("About &Qt", self,
-                                       triggered=qApp.aboutQt)
+                                       triggered=qApp.aboutQt)  # noqa: F821
 
     def create_menus(self):
         self.file_menu = self.menuBar().addMenu("&File")
@@ -508,7 +505,7 @@ class SettingsTree(QTreeWidget):
         # The signal might not be connected.
         try:
             self.itemChanged.disconnect(self.update_setting)
-        except:
+        except Exception:
             pass
 
         self.settings.sync()
@@ -531,7 +528,6 @@ class SettingsTree(QTreeWidget):
             key = ancestor.text(0) + '/' + key
             ancestor = ancestor.parent()
 
-        d = item.data(2, Qt.UserRole)
         self.settings.setValue(key, item.data(2, Qt.UserRole))
 
         if self.auto_refresh:
@@ -703,7 +699,7 @@ class VariantDelegate(QItemDelegate):
             value = editor.value()
         else:
             value = self.value_from_lineedit(editor, model, index)
-        if not value is None:
+        if value is not None:
             model.setData(index, value, Qt.UserRole)
             model.setData(index, self.display_text(value), Qt.DisplayRole)
 
index b8cab06ef7a71ca226f992652bb45e56c95bf96c..4689813d415e46bb7ea5f0570ee57bba4c260ccd 100644 (file)
@@ -30,7 +30,7 @@ INFO_KEY = 'info'
 
 
 HELP = ("Use mouse wheel or the '+' and '-' keys to zoom. Press and "
-       "hold left mouse button to scroll.")
+        "hold left mouse button to scroll.")
 
 
 class RenderThread(QThread):
@@ -53,7 +53,8 @@ class RenderThread(QThread):
         self.abort = False
 
         for i in range(RenderThread.colormap_size):
-            self.colormap.append(self.rgb_from_wave_length(380.0 + (i * 400.0 / RenderThread.colormap_size)))
+            self.colormap.append(
+                self.rgb_from_wave_length(380.0 + (i * 400.0 / RenderThread.colormap_size)))
 
     def stop(self):
         self.mutex.lock()
@@ -132,7 +133,8 @@ class RenderThread(QThread):
 
                         if num_iterations < max_iterations:
                             image.setPixel(x + half_width, y + half_height,
-                                           self.colormap[num_iterations % RenderThread.colormap_size])
+                                           self.colormap[
+                                               num_iterations % RenderThread.colormap_size])
                             all_black = False
                         else:
                             image.setPixel(x + half_width, y + half_height, qRgb(0, 0, 0))
@@ -221,7 +223,7 @@ class MandelbrotWidget(QWidget):
             if self.pixmap.isNull():
                 painter.setPen(Qt.white)
                 painter.drawText(self.rect(), Qt.AlignCenter,
-                        "Rendering initial image, please wait...")
+                                 "Rendering initial image, please wait...")
                 return
 
             if self._cur_scale == self._pixmap_scale:
@@ -250,10 +252,10 @@ class MandelbrotWidget(QWidget):
             painter.setPen(Qt.NoPen)
             painter.setBrush(QColor(0, 0, 0, 127))
             painter.drawRect((self.width() - text_width) / 2 - 5, 0, text_width + 10,
-                    metrics.lineSpacing() + 5)
+                             metrics.lineSpacing() + 5)
             painter.setPen(Qt.white)
             painter.drawText((self.width() - text_width) / 2,
-                    metrics.leading() + metrics.ascent(), text)
+                             metrics.leading() + metrics.ascent(), text)
 
     def resizeEvent(self, event):
         self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
@@ -302,7 +304,7 @@ class MandelbrotWidget(QWidget):
             delta_y = (self.height() - self.pixmap.height()) / 2 - self._pixmap_offset.y()
             self.scroll(delta_x, delta_y)
 
-    @Slot(QImage,float)
+    @Slot(QImage, float)
     def update_pixmap(self, image, scale_factor):
         if not self._last_drag_pos.isNull():
             return
@@ -317,15 +319,13 @@ class MandelbrotWidget(QWidget):
     def zoom(self, zoomFactor):
         self._cur_scale *= zoomFactor
         self.update()
-        self.thread.render(self._center_x, self._center_y, self._cur_scale,
-                self.size())
+        self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
 
     def scroll(self, deltaX, deltaY):
         self._center_x += deltaX * self._cur_scale
         self._center_y += deltaY * self._cur_scale
         self.update()
-        self.thread.render(self._center_x, self._center_y, self._cur_scale,
-                self.size())
+        self.thread.render(self._center_x, self._center_y, self._cur_scale, self.size())
 
 
 if __name__ == '__main__':
index 6c61d6708fc30cd85ca9a499ce36601b56641ecb..7938a5ca153d49a77a6f6c4cdf999f0e9cd48657 100644 (file)
@@ -8,8 +8,7 @@ from PySide6.QtGui import QFont
 from PySide6.QtWidgets import (QButtonGroup, QCheckBox, QComboBox, QFontComboBox,
                                QLabel, QPushButton, QHBoxLayout, QSizePolicy,
                                QRadioButton, QSlider, QVBoxLayout, QWidget)
-from PySide6.QtDataVisualization import (QAbstract3DGraph, QAbstract3DSeries,
-                                         Q3DBars)
+from PySide6.QtDataVisualization import (QAbstract3DGraph, QAbstract3DSeries, Q3DBars)
 
 
 class BarGraph(QObject):
@@ -92,18 +91,14 @@ class BarGraph(QObject):
         selectionModeList.addItem("Slice into Row and Item", sel)
         sel = QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionColumn
         selectionModeList.addItem("Slice into Column", sel)
-        sel = (QAbstract3DGraph.SelectionSlice
-               | QAbstract3DGraph.SelectionItemAndColumn)
+        sel = (QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndColumn)
         selectionModeList.addItem("Slice into Column and Item", sel)
-        sel = (QAbstract3DGraph.SelectionItemRowAndColumn
-               | QAbstract3DGraph.SelectionMultiSeries)
+        sel = (QAbstract3DGraph.SelectionItemRowAndColumn | QAbstract3DGraph.SelectionMultiSeries)
         selectionModeList.addItem("Multi: Bar, Row, Col", sel)
-        sel = (QAbstract3DGraph.SelectionSlice
-               | QAbstract3DGraph.SelectionItemAndRow
+        sel = (QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndRow
                | QAbstract3DGraph.SelectionMultiSeries)
         selectionModeList.addItem("Multi, Slice: Row, Item", sel)
-        sel = (QAbstract3DGraph.SelectionSlice
-               | QAbstract3DGraph.SelectionItemAndColumn
+        sel = (QAbstract3DGraph.SelectionSlice | QAbstract3DGraph.SelectionItemAndColumn
                | QAbstract3DGraph.SelectionMultiSeries)
         selectionModeList.addItem("Multi, Slice: Col, Item", sel)
         selectionModeList.setCurrentIndex(1)
index 66811724579c4e839722ce353d2d5f9522fba366..4b57b85ddce934ea75acfe78b926194d8582f2ce 100644 (file)
@@ -34,8 +34,8 @@ if __name__ == "__main__":
     surface = SurfaceGraph()
 
     if (not bars.initialize(minimum_graph_size, screen_size)
-        or not scatter.initialize(minimum_graph_size, screen_size)
-        or not surface.initialize(minimum_graph_size, screen_size)):
+            or not scatter.initialize(minimum_graph_size, screen_size)
+            or not surface.initialize(minimum_graph_size, screen_size)):
         QMessageBox.warning(None, "Graph Gallery", "Couldn't initialize the OpenGL context.")
         sys.exit(-1)
 
index 2b6f5d84e9c0d3cbfb3da796d17149c1e5a50bd9..f69ebaf8049dd988a5c9df88b11c201cb518dc63 100644 (file)
@@ -64,8 +64,8 @@ class VariantBarDataProxy(QBarDataProxy):
         # If we have no data or mapping, or the categories are not defined,
         # simply clear the array
         if (not self._dataSet or not self._mapping
-            or not self._mapping.rowCategories()
-            or not self._mapping.columnCategories()):
+                or not self._mapping.rowCategories()
+                or not self._mapping.columnCategories()):
             self.resetArray()
             return
 
index a02e060cd178adf0d32ff9f2e5733087acbbe848..7b980bc1708a06916b44ad61b931a2132d48732b 100644 (file)
@@ -12,8 +12,8 @@ from PySide6.QtGui import QGuiApplication
 from PySide6.QtQuick import QQuickView
 from PySide6.QtDataVisualization import qDefaultSurfaceFormat
 
-from datasource import DataSource
-import rc_qmlsurfacegallery
+from datasource import DataSource  # noqa: F401
+import rc_qmlsurfacegallery  # noqa: F401
 
 
 if __name__ == "__main__":
index 8dd741c0e578883fc426ca0f94bae8d07bc2efa5..d61f2549911a7c87c820c39023f69d9064da742d 100644 (file)
@@ -5,7 +5,7 @@
 
 import sys
 from PySide6.QtCore import QCoreApplication
-from PySide6.QtDBus import QDBusConnection,  QDBusInterface, QDBusReply
+from PySide6.QtDBus import QDBusConnection, QDBusInterface, QDBusReply
 
 
 SERVICE_NAME = 'org.example.QtDBus.PingExample'
@@ -36,4 +36,3 @@ if __name__ == "__main__":
     value = reply.value()
     print(f'ping: Reply was: {value}')
     sys.exit(0)
-
index cdb610c505b1421d6e94ace261db149e78340a39..0dec6eda08422554445fd40891146971fd074671 100644 (file)
@@ -18,7 +18,7 @@ class Pong(QObject):
     @Slot(str, result=str)
     def ping(self, arg):
         print(f'pong: Received ping({arg})')
-        qApp.quit()
+        qApp.quit()  # noqa: F821
         return f'ping("{arg}") got called'
 
 
index 99202bf754082d5b932a1bf26df231834fce5059..e9abd0bec150321aa02c5b31de498abc74e5772b 100644 (file)
@@ -1,8 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-from PySide6.QtWidgets import (QDialog, QFileDialog, QMainWindow,
-                               QMessageBox, QToolButton)
+from PySide6.QtWidgets import (QDialog, QFileDialog, QMainWindow, QMessageBox)
 from PySide6.QtCore import (QDir, QFile, QFileInfo, QSettings, Slot)
 
 from ui_mainwindow import Ui_MainWindow
index 0762cfdce80a1ef272e1955d059ed4312d8f8fd3..63d25bf702b93e735274fa48e134d621c9e1b11e 100644 (file)
@@ -48,7 +48,7 @@ class PdfViewer(AbstractViewer):
         nav = self._pdfView.pageNavigator()
         self._pageSelector = QPdfPageSelector(self._toolBar)
         self._toolBar.insertWidget(self._uiAssets_forward, self._pageSelector)
-        self._pageSelector.setDocument(self._document);
+        self._pageSelector.setDocument(self._document)
         self._pageSelector.currentPageChanged.connect(self.pageSelected)
         nav.currentPageChanged.connect(self._pageSelector.setCurrentPage)
         nav.backAvailableChanged.connect(self._uiAssets_back.setEnabled)
index 7bcb4c0efc2eda9b11950851a441a4798c9b011c..fdfc56d408b387ee490f323b2e7c4b4f9ebe70f1 100644 (file)
@@ -3,7 +3,7 @@
 
 from enum import Enum, auto
 
-from PySide6.QtCore import QFile, QFileInfo, QObject, QSettings, Signal, Slot
+from PySide6.QtCore import QFileInfo, QObject, QSettings, Signal, Slot
 
 
 DEFAULT_MAX_FILES = 10
index 32a916be380aac8284c165a376b6ab586a7914c5..ecae6770bf17f1822107a8f38ec60d4d47102b79 100644 (file)
@@ -65,7 +65,7 @@ class ViewerFactory:
         list = []
         for name, viewer in self._viewers.items():
             if ((self._defaultViewer and viewer.isDefaultViewer())
-                or (not self._defaultViewer and name == "TxtViewer")):
+                    or (not self._defaultViewer and name == "TxtViewer")):
                 name += "(default)"
             list.append(name)
         return list
index 4f163e374bce5834f27583f3bb72e1fcc460e916..2b014c790cad167c85ae940f80757fd1824a2a9a 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-from tictactoe import TicTacToe
+from tictactoe import TicTacToe  # noqa: F401
 from tictactoeplugin import TicTacToePlugin
 
 from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
index 9155d2309b402b0eaa69ceddd3c7d20107898964..aa1c3158c8cfe020b12a135b8be805b5906ea8b7 100644 (file)
@@ -89,27 +89,27 @@ class TicTacToe(QWidget):
 
             for position in range(0, 8, 3):
                 if (self._state[position] != EMPTY
-                    and self._state[position + 1] == self._state[position]
-                    and self._state[position + 2] == self._state[position]):
+                        and self._state[position + 1] == self._state[position]
+                        and self._state[position + 2] == self._state[position]):
                     y = self._cell_rect(position).center().y()
                     painter.drawLine(0, y, self.width(), y)
                     self._turn_number = 9
 
             for position in range(3):
                 if (self._state[position] != EMPTY
-                    and self._state[position + 3] == self._state[position]
-                    and self._state[position + 6] == self._state[position]):
+                        and self._state[position + 3] == self._state[position]
+                        and self._state[position + 6] == self._state[position]):
                     x = self._cell_rect(position).center().x()
                     painter.drawLine(x, 0, x, self.height())
                     self._turn_number = 9
 
             if (self._state[0] != EMPTY and self._state[4] == self._state[0]
-                and self._state[8] == self._state[0]):
+                    and self._state[8] == self._state[0]):
                 painter.drawLine(0, 0, self.width(), self.height())
                 self._turn_number = 9
 
             if (self._state[2] != EMPTY and self._state[4] == self._state[2]
-                and self._state[6] == self._state[2]):
+                    and self._state[6] == self._state[2]):
                 painter.drawLine(0, self.height(), self.width(), 0)
                 self._turn_number = 9
 
index 0d538443f3812314cafbfa062937de5bd6a2a11d..f9c925133a62dd3c4505020ac8d1c562c24c2ba8 100644 (file)
@@ -5,7 +5,7 @@ from tictactoe import TicTacToe
 from tictactoetaskmenu import TicTacToeTaskMenuFactory
 
 from PySide6.QtGui import QIcon
-from PySide6.QtDesigner import  QDesignerCustomWidgetInterface
+from PySide6.QtDesigner import QDesignerCustomWidgetInterface
 
 
 DOM_XML = """
index bed6c7ef5f71d4252051cf6f8b9531144d111937..8bfcc4ca22300d4ccedfcbb85e078ffc66951aa4 100644 (file)
@@ -32,12 +32,12 @@ class ApplicationWindow(QMainWindow):
         # Main menu bar
         self.menu = self.menuBar()
         self.menu_file = self.menu.addMenu("File")
-        exit = QAction("Exit", self, triggered=qApp.quit)
+        exit = QAction("Exit", self, triggered=qApp.quit)  # noqa: F821
         self.menu_file.addAction(exit)
 
         self.menu_about = self.menu.addMenu("&About")
         about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
-                        triggered=qApp.aboutQt)
+                        triggered=qApp.aboutQt)  # noqa: F821
         self.menu_about.addAction(about)
 
         # Figure (Left)
index a3a1e3782e27e56daaae414595782ca3d586fd5c..0c55a13336ce92e4aefad8c8a415a13e24aff9f5 100644 (file)
@@ -74,12 +74,12 @@ class Window(QMainWindow):
         # Main menu bar
         self.menu = self.menuBar()
         self.menu_file = self.menu.addMenu("File")
-        exit = QAction("Exit", self, triggered=qApp.quit)
+        exit = QAction("Exit", self, triggered=qApp.quit)  # noqa: F821
         self.menu_file.addAction(exit)
 
         self.menu_about = self.menu.addMenu("&About")
         about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
-                        triggered=qApp.aboutQt)
+                        triggered=qApp.aboutQt)  # noqa: F821
         self.menu_about.addAction(about)
 
         # Create a label for the display camera
index b0aa143e2cf43fd01e95c443b82d835acafe50e5..94fdc3bdc91b7c48826a68b64e8d5d3e6fe3b8e7 100644 (file)
@@ -32,12 +32,12 @@ class ApplicationWindow(QMainWindow):
         # Main menu bar
         self.menu = self.menuBar()
         self.menu_file = self.menu.addMenu("File")
-        exit = QAction("Exit", self, triggered=qApp.quit)
+        exit = QAction("Exit", self, triggered=qApp.quit)  # noqa: F821
         self.menu_file.addAction(exit)
 
         self.menu_about = self.menu.addMenu("&About")
         about = QAction("About Qt", self, shortcut=QKeySequence(QKeySequence.HelpContents),
-                        triggered=qApp.aboutQt)
+                        triggered=qApp.aboutQt)  # noqa: F821
         self.menu_about.addAction(about)
 
         # Create an artificial color close to the original one
index 4f773e3cb5006f6c01a3a5c00954122490ffac79..7bb2238a792e2c641f2c362b14e71e700203a290 100644 (file)
@@ -3,7 +3,6 @@
 
 """PySide6 port of the Qt Graphs widgetgallery example from Qt v6.x"""
 
-import os
 import sys
 
 from PySide6.QtCore import QSize
index 743dafccaff0332e6907cea8867616f3b388c65f..5ab2a2cd2e6d32d11e5a9697b13c1f28b61835a5 100644 (file)
@@ -64,8 +64,8 @@ class VariantBarDataProxy(QBarDataProxy):
         # If we have no data or mapping, or the categories are not defined,
         # simply clear the array
         if (not self._dataSet or not self._mapping
-            or not self._mapping.rowCategories()
-            or not self._mapping.columnCategories()):
+                or not self._mapping.rowCategories()
+                or not self._mapping.columnCategories()):
             self.resetArray()
             return
 
index 7a5064a6e768b65a1adf1f661d93f9c37eb094c1..c0e00dd93204d7a11c404ff2957f81f611e8721d 100644 (file)
@@ -4,8 +4,7 @@
 import sys
 
 from PySide6.QtCore import QPoint, QTimer, QTime, Qt
-from PySide6.QtGui import (QColor, QGradient, QGuiApplication, QPainter,
-                           QPalette, QPolygon, QRasterWindow)
+from PySide6.QtGui import QGuiApplication, QPainter, QPalette, QPolygon, QRasterWindow
 
 """Simplified PySide6 port of the gui/analogclock example from Qt v6.x"""
 
@@ -28,7 +27,7 @@ class AnalogClockWindow(QRasterWindow):
         self._seconds_hand = QPolygon([QPoint(1, 14), QPoint(-1, 14),
                                        QPoint(-1, -89), QPoint(1, -89)])
 
-        palette = qApp.palette()
+        palette = qApp.palette()  # noqa: F821
         self._background_color = palette.color(QPalette.Window)
         self._hour_color = palette.color(QPalette.Text)
         self._minute_color = palette.color(QPalette.Text)
@@ -59,7 +58,7 @@ class AnalogClockWindow(QRasterWindow):
         painter.drawConvexPolygon(self._hour_hand)
         painter.restore()
 
-        for i in range(0, 12):
+        for _ in range(0, 12):
             painter.drawRect(73, -3, 16, 6)
             painter.rotate(30.0)
 
@@ -81,7 +80,7 @@ class AnalogClockWindow(QRasterWindow):
 
         painter.setPen(self._minute_color)
 
-        for j in range(0, 60):
+        for _ in range(0, 60):
             painter.drawLine(92, 0, 96, 0)
             painter.rotate(6.0)
 
index 261adf381465f10835951db9bfdd3edb82eec5fc..cfb73b10bdfb67bcdc14afa36e7e00b7a1aca446 100644 (file)
@@ -8,7 +8,7 @@ from PySide6.QtCore import QCoreApplication
 from PySide6.QtGui import QGuiApplication, QRhi, QSurfaceFormat
 
 from rhiwindow import HelloWindow
-import rc_rhiwindow
+import rc_rhiwindow  # noqa: F401
 
 if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
index dff56fec8015971fab818833c4adb0c346a5edc4..fe054af4883d7223b6723c92b776e029a3c0b68d 100644 (file)
@@ -5,11 +5,10 @@ import numpy
 import sys
 
 from PySide6.QtCore import (QEvent, QFile, QIODevice, QPointF, QRectF, QSize,
-                            QSizeF, qFatal, qWarning, Qt)
+                            qFatal, qWarning, Qt)
 from PySide6.QtGui import (QColor, QFont, QGradient, QImage, QMatrix4x4,
-                           QOffscreenSurface, QPainter, QPlatformSurfaceEvent,
-                           QSurface, QWindow)
-from PySide6.QtGui import (QRhi, QRhiBuffer, QRhiCommandBuffer,
+                           QPainter, QPlatformSurfaceEvent, QSurface, QWindow)
+from PySide6.QtGui import (QRhi, QRhiBuffer,
                            QRhiDepthStencilClearValue,
                            QRhiGraphicsPipeline, QRhiNullInitParams,
                            QRhiGles2InitParams, QRhiRenderBuffer,
@@ -28,9 +27,9 @@ elif sys.platform == "darwin":
 
 # Y up (note clipSpaceCorrMatrix in m_viewProjection), CCW
 VERTEX_DATA = numpy.array([
-      0.0,  0.5, 1.0, 0.0, 0.0,
-     -0.5, -0.5, 0.0, 1.0, 0.0,
-      0.5, -0.5, 0.0, 0.0, 1.0], dtype=numpy.float32)
+    0.0, 0.5, 1.0, 0.0, 0.0,
+    -0.5, -0.5, 0.0, 1.0, 0.0,
+    0.5, -0.5, 0.0, 0.0, 1.0], dtype=numpy.float32)
 
 
 UBUF_SIZE = 68
@@ -124,7 +123,8 @@ class RhiWindow(QWindow):
         surfaceSize = self.m_sc.surfacePixelSize() if self.m_hasSwapChain else QSize()
 
         # stop pushing frames when not exposed (or size is 0)
-        if (not is_exposed or (self.m_hasSwapChain and surfaceSize.isEmpty())) and self.m_initialized and not self.m_notExposed:
+        if ((not is_exposed or (self.m_hasSwapChain and surfaceSize.isEmpty()))
+                and self.m_initialized and not self.m_notExposed):
             self.m_notExposed = True
 
         # Continue when exposed again and the surface has a valid size. Note
index a9819477b48ad3f90576df3dee9fdc694a3c8679..3921b5d511effb16b3cadee40064d1650164e4d2 100644 (file)
@@ -31,7 +31,7 @@ class MyWidget(QWidget):
         super().__init__()
 
         self.hello = ["Hallo Welt", "你好,世界", "Hei maailma",
-            "Hola Mundo", "Привет мир"]
+                      "Hola Mundo", "Привет мир"]
 
         self.button = QPushButton("Click me!")
         self.text = QLabel(f"Hello World auto_quit={auto_quit}")
index d30544991d4970e41b6966159ffbf7a781103212..418e2e77e4a196d59037f568f04e4b92fe136544 100644 (file)
@@ -1,6 +1,8 @@
 Map Viewer Example
 ==================
 
+.. tags:: Android
+
 The Map Viewer example shows how to display and interact with a map,
 search for an address, and find driving directions.
 
index e0634e75116ed2c3bb9fd28feaac4653a3110d0a..5be9b0d6e09b6f997aeb7b1b7bd8bbe0051f906a 100644 (file)
@@ -40,24 +40,23 @@ class Generator(QIODevice):
         sample_size = fmt.bytesPerSample() * 8
         if sample_size == 8:
             if fmt.sampleFormat() == QAudioFormat.UInt8:
-                scaler = lambda x: ((1.0 + x) / 2 * 255)
+                scaler = lambda x: ((1.0 + x) / 2 * 255)  # noqa: E731
                 pack_format = 'B'
             elif fmt.sampleFormat() == QAudioFormat.Int16:
-                scaler = lambda x: x * 127
+                scaler = lambda x: x * 127  # noqa: E731
                 pack_format = 'b'
         elif sample_size == 16:
             little_endian = QSysInfo.ByteOrder == QSysInfo.LittleEndian
             if fmt.sampleFormat() == QAudioFormat.UInt8:
-                scaler = lambda x: (1.0 + x) / 2 * 65535
+                scaler = lambda x: (1.0 + x) / 2 * 65535  # noqa: E731
                 pack_format = '<H' if little_endian else '>H'
             elif fmt.sampleFormat() == QAudioFormat.Int16:
-                scaler = lambda x: x * 32767
+                scaler = lambda x: x * 32767  # noqa: E731
                 pack_format = '<h' if little_endian else '>h'
 
-        assert(pack_format != '')
+        assert pack_format != ''
 
         channel_bytes = fmt.bytesPerSample()
-        sample_bytes = fmt.channelCount() * channel_bytes
 
         length = (fmt.sampleRate() * fmt.channelCount() * channel_bytes) * durationUs // 100000
 
@@ -133,16 +132,14 @@ class AudioTest(QMainWindow):
 
         layout.addWidget(self.m_modeButton)
 
-        self.m_suspendResumeButton = QPushButton(
-                clicked=self.toggle_suspend_resume)
+        self.m_suspendResumeButton = QPushButton(clicked=self.toggle_suspend_resume)
         self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
 
         layout.addWidget(self.m_suspendResumeButton)
 
         volume_box = QHBoxLayout()
         volume_label = QLabel("Volume:")
-        self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100,
-                singleStep=10)
+        self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100, singleStep=10)
         self.m_volumeSlider.valueChanged.connect(self.volume_changed)
 
         volume_box.addWidget(volume_label)
@@ -167,8 +164,8 @@ class AudioTest(QMainWindow):
             qWarning("Default format not supported - trying to use nearest")
             self.m_format = info.nearestFormat(self.m_format)
 
-        self.m_generator = Generator(self.m_format,
-                self.DURATION_SECONDS * 1000000, self.TONE_SAMPLE_RATE_HZ, self)
+        self.m_generator = Generator(self.m_format, self.DURATION_SECONDS * 1000000,
+                                     self.TONE_SAMPLE_RATE_HZ, self)
 
         self.create_audio_output()
 
index bbd71c5d816b245a1f78faab9d5904a322fbb1fb..fac7e33e1e27c6e660b0b58460894f262a8d7c6f 100644 (file)
@@ -1,6 +1,8 @@
 Audio Output Example
 ====================
 
+.. tags:: Android
+
 Audio Output demonstrates the basic use cases of QAudioOutput.
 
 This example provides a tone generator to supply continuous audio playback. The
index 555e0b9378341825935779c678ee96f28658e599..bc2d606c09341fa5c370a087eef3e6582f486e94 100644 (file)
@@ -19,27 +19,15 @@ from typing import Optional
 import PySide6
 from PySide6.QtCore import QByteArray, QMargins, Qt, Slot, qWarning
 from PySide6.QtGui import QPainter, QPalette
-from PySide6.QtMultimedia import (
-    QAudio,
-    QAudioDevice,
-    QAudioFormat,
-    QAudioSource,
-    QMediaDevices,
-)
-from PySide6.QtWidgets import (
-    QApplication,
-    QComboBox,
-    QPushButton,
-    QSlider,
-    QVBoxLayout,
-    QWidget,
-    QLabel
-)
+from PySide6.QtMultimedia import QAudio, QAudioDevice, QAudioFormat, QAudioSource, QMediaDevices
+from PySide6.QtWidgets import (QApplication, QComboBox, QPushButton, QSlider, QVBoxLayout,
+                               QWidget, QLabel)
 
 is_android = os.environ.get('ANDROID_ARGUMENT')
 
 if is_android:
-    from PySide6.QtCore import QCoreApplication, QMicrophonePermission
+    from PySide6.QtCore import QMicrophonePermission
+
 
 class AudioInfo:
     def __init__(self, format: QAudioFormat):
@@ -107,9 +95,9 @@ class InputTest(QWidget):
     def initialize(self):
         if is_android:
             permission = QMicrophonePermission()
-            permission_status = qApp.checkPermission(permission)
+            permission_status = qApp.checkPermission(permission)  # noqa: F821
             if permission_status == Qt.PermissionStatus.Undetermined:
-                qApp.requestPermission(permission, self, self.initialize)
+                qApp.requestPermission(permission, self, self.initialize)  # noqa: F821
                 return
             if permission_status == Qt.PermissionStatus.Denied:
                 qWarning("Microphone permission is not granted!")
index b1dcd61a8b4b82dace8f3383f7fcd7688ea61635..3a247c503006c3879957ae23b26e9b4b84ce9f95 100644 (file)
@@ -1,6 +1,8 @@
 Audio Source Example
 ====================
 
+.. tags:: Android
+
 A Python application that demonstrates the analogous example in C++
 `Audio Source Example <https://doc-snapshots.qt.io/qt6-dev/qtmultimedia-multimedia-audiosource-example.html>`_
 
index c1ea92faaa471a7fa5ce4b6d207a07afce58d89a..f66f28a21c8136fd27a4baa07a3eeee59a374bbd 100644 (file)
@@ -17,7 +17,7 @@ from imagesettings import ImageSettings
 from videosettings import VideoSettings, is_android
 
 if is_android:
-    from PySide6.QtCore import QCoreApplication, QMicrophonePermission, QCameraPermission
+    from PySide6.QtCore import QMicrophonePermission, QCameraPermission
     from ui_camera_mobile import Ui_Camera
 else:
     from ui_camera import Ui_Camera
@@ -45,7 +45,7 @@ class Camera(QMainWindow):
         image = Path(__file__).parent / "shutter.svg"
         self._ui.takeImageButton.setIcon(QIcon(os.fspath(image)))
         if not is_android:
-            self._ui.actionAbout_Qt.triggered.connect(qApp.aboutQt)
+            self._ui.actionAbout_Qt.triggered.connect(qApp.aboutQt)  # noqa: F821
 
         # disable all buttons by default
         self.updateCameraActive(False)
@@ -63,9 +63,9 @@ class Camera(QMainWindow):
         if is_android:
             # camera
             cam_permission = QCameraPermission()
-            cam_permission_status = qApp.checkPermission(cam_permission)
+            cam_permission_status = qApp.checkPermission(cam_permission)  # noqa: F821
             if cam_permission_status == Qt.PermissionStatus.Undetermined:
-                qApp.requestPermission(cam_permission, self, self.initialize)
+                qApp.requestPermission(cam_permission, self, self.initialize)  # noqa: F821
                 return
             if cam_permission_status == Qt.PermissionStatus.Denied:
                 qWarning("Camera permission is not granted!")
@@ -75,9 +75,9 @@ class Camera(QMainWindow):
 
             # microphone
             microphone_permission = QMicrophonePermission()
-            microphone_permission_status = qApp.checkPermission(microphone_permission)
+            microphone_permission_status = qApp.checkPermission(microphone_permission)  # noqa: F821
             if microphone_permission_status == Qt.PermissionStatus.Undetermined:
-                qApp.requestPermission(microphone_permission, self, self.initialize)
+                qApp.requestPermission(microphone_permission, self, self.initialize)  # noqa: F821
                 return
             if microphone_permission_status == Qt.PermissionStatus.Denied:
                 qWarning("Microphone permission is not granted!")
index 8ef9f6700ea5e746715588659e6f4cdf1e790b69..7fc75a387f8b149d7de97e5bba78788601deff84 100644 (file)
@@ -1,6 +1,8 @@
 Camera Example
 ===============
 
+.. tags:: Android
+
 The Camera Example shows how to use the API to capture a still image or video.
 
 The Camera Example demonstrates how you can use Qt Multimedia to implement some
index 2359bd55b7f1c0fd90ccde74ea90683e2bbcb2d1..a88cb39ed20d3c2877d965f85f9e60ae86fe955d 100644 (file)
@@ -14,7 +14,6 @@ else:
     from ui_videosettings import Ui_VideoSettingsUi
 
 
-
 def box_value(box):
     idx = box.currentIndex()
     return None if idx == -1 else box.itemData(idx)
index 88be50cc35150055d5901dcff2f3be1feb904235..4731386ad84d808d656c5b60b76caff9a77a4038 100644 (file)
@@ -7,7 +7,7 @@ import sys
 from PySide6.QtCore import QStandardPaths, Qt, Slot
 from PySide6.QtGui import QAction, QIcon, QKeySequence
 from PySide6.QtWidgets import (QApplication, QDialog, QFileDialog,
-    QMainWindow, QSlider, QStyle, QToolBar)
+                               QMainWindow, QSlider, QStyle, QToolBar)
 from PySide6.QtMultimedia import (QAudioOutput, QMediaFormat,
                                   QMediaPlayer)
 from PySide6.QtMultimediaWidgets import QVideoWidget
@@ -100,7 +100,7 @@ class MainWindow(QMainWindow):
         tool_bar.addWidget(self._volume_slider)
 
         about_menu = self.menuBar().addMenu("&About")
-        about_qt_act = QAction("About &Qt", self, triggered=qApp.aboutQt)
+        about_qt_act = QAction("About &Qt", self, triggered=qApp.aboutQt)  # noqa: F821
         about_menu.addAction(about_qt_act)
 
         self._video_widget = QVideoWidget()
@@ -168,8 +168,7 @@ class MainWindow(QMainWindow):
     @Slot("QMediaPlayer::PlaybackState")
     def update_buttons(self, state):
         media_count = len(self._playlist)
-        self._play_action.setEnabled(media_count > 0
-            and state != QMediaPlayer.PlayingState)
+        self._play_action.setEnabled(media_count > 0 and state != QMediaPlayer.PlayingState)
         self._pause_action.setEnabled(state == QMediaPlayer.PlayingState)
         self._stop_action.setEnabled(state != QMediaPlayer.StoppedState)
         self._previous_action.setEnabled(self._player.position() > 0)
index d3553aa274873d29de73a814a393c4f1601b43a4..72bb306e3f69de1988c876fc1094d5b93906bb64 100644 (file)
@@ -9,7 +9,7 @@ class ScreenListModel(QAbstractListModel):
 
     def __init__(self, parent=None):
         super().__init__(parent)
-        app = qApp
+        app = qApp  # noqa: F821
         app.screenAdded.connect(self.screens_changed)
         app.screenRemoved.connect(self.screens_changed)
         app.primaryScreenChanged.connect(self.screens_changed)
index f467cf8d4ba3a04e808149a66645c3cd104f5ca8..d0dd7e0ad632ddc76c4e20bd1776d1c76f17fd2e 100644 (file)
@@ -5,12 +5,13 @@
 """PySide6 port of the network/blockingfortunclient example from Qt v5.x, originating from PyQt"""
 
 from PySide6.QtCore import (Signal, QDataStream, QMutex, QMutexLocker,
-        QThread, QWaitCondition)
+                            QThread, QWaitCondition)
 from PySide6.QtGui import QIntValidator
 from PySide6.QtWidgets import (QApplication, QDialogButtonBox, QGridLayout,
-        QLabel, QLineEdit, QMessageBox, QPushButton, QWidget)
+                               QLabel, QLineEdit, QMessageBox, QPushButton,
+                               QWidget)
 from PySide6.QtNetwork import (QAbstractSocket, QHostAddress, QNetworkInterface,
-        QTcpSocket)
+                               QTcpSocket)
 
 
 class FortuneThread(QThread):
@@ -109,7 +110,7 @@ class BlockingClient(QWidget):
         port_label.setBuddy(self._port_line_edit)
 
         self._status_label = QLabel(
-                "This example requires that you run the Fortune Server example as well.")
+            "This example requires that you run the Fortune Server example as well.")
         self._status_label.setWordWrap(True)
 
         self._get_fortune_button = QPushButton("Get Fortune")
@@ -144,7 +145,7 @@ class BlockingClient(QWidget):
     def request_new_fortune(self):
         self._get_fortune_button.setEnabled(False)
         self.thread.request_new_fortune(self._host_line_edit.text(),
-                int(self._port_line_edit.text()))
+                                        int(self._port_line_edit.text()))
 
     def show_fortune(self, nextFortune):
         if nextFortune == self._current_fortune:
@@ -158,22 +159,22 @@ class BlockingClient(QWidget):
     def display_error(self, socketError, message):
         if socketError == QAbstractSocket.HostNotFoundError:
             QMessageBox.information(self, "Blocking Fortune Client",
-                    "The host was not found. Please check the host and port "
-                    "settings.")
+                                    "The host was not found. Please check the host and port "
+                                    "settings.")
         elif socketError == QAbstractSocket.ConnectionRefusedError:
             QMessageBox.information(self, "Blocking Fortune Client",
-                    "The connection was refused by the peer. Make sure the "
-                    "fortune server is running, and check that the host name "
-                    "and port settings are correct.")
+                                    "The connection was refused by the peer. Make sure the "
+                                    "fortune server is running, and check that the host name "
+                                    "and port settings are correct.")
         else:
             QMessageBox.information(self, "Blocking Fortune Client",
-                    f"The following error occurred: {message}.")
+                                    f"The following error occurred: {message}.")
 
         self._get_fortune_button.setEnabled(True)
 
     def enable_get_fortune_button(self):
-        self._get_fortune_button.setEnabled(self._host_line_edit.text() != '' and
-                self._port_line_edit.text() != '')
+        self._get_fortune_button.setEnabled(self._host_line_edit.text() != ''
+                                            and self._port_line_edit.text() != '')
 
 
 if __name__ == '__main__':
index f989adf30138bd155e4970c0821cb1eb9788a1c8..fba0cb980910ff4131bac1663541440470972e64 100644 (file)
@@ -35,7 +35,8 @@ class DownloaderWidget(QWidget):
         self.link_box.setPlaceholderText("Download Link ...")
 
         self._open_folder_action = self.dest_box.addAction(
-            qApp.style().standardIcon(QStyle.SP_DirOpenIcon), QLineEdit.TrailingPosition
+            qApp.style().standardIcon(QStyle.SP_DirOpenIcon),  # noqa: F821
+            QLineEdit.TrailingPosition
         )
         self._open_folder_action.triggered.connect(self.on_open_folder)
 
index b695c2ea8f1d27d6585ef94131f9c3109ed95750..e88e5e35b132b244336936f00a6a82e1b75c6d0e 100644 (file)
@@ -31,7 +31,7 @@ class Client(QDialog):
         port_label.setBuddy(self._port_line_edit)
 
         self._status_label = QLabel("This examples requires that you run "
-                "the Fortune Server example as well.")
+                                    "the Fortune Server example as well.")
 
         self._get_fortune_button = QPushButton("Get Fortune")
         self._get_fortune_button.setDefault(True)
@@ -40,8 +40,7 @@ class Client(QDialog):
         quit_button = QPushButton("Quit")
 
         button_box = QDialogButtonBox()
-        button_box.addButton(self._get_fortune_button,
-                QDialogButtonBox.ActionRole)
+        button_box.addButton(self._get_fortune_button, QDialogButtonBox.ActionRole)
         button_box.addButton(quit_button, QDialogButtonBox.RejectRole)
 
         self._tcp_socket = QTcpSocket(self)
@@ -69,7 +68,7 @@ class Client(QDialog):
         self._block_size = 0
         self._tcp_socket.abort()
         self._tcp_socket.connectToHost(self._host_line_edit.text(),
-                int(self._port_line_edit.text()))
+                                       int(self._port_line_edit.text()))
 
     def read_fortune(self):
         instr = QDataStream(self._tcp_socket)
@@ -99,23 +98,23 @@ class Client(QDialog):
             pass
         elif socketError == QAbstractSocket.HostNotFoundError:
             QMessageBox.information(self, "Fortune Client",
-                    "The host was not found. Please check the host name and "
-                    "port settings.")
+                                    "The host was not found. Please check the host name and "
+                                    "port settings.")
         elif socketError == QAbstractSocket.ConnectionRefusedError:
             QMessageBox.information(self, "Fortune Client",
-                    "The connection was refused by the peer. Make sure the "
-                    "fortune server is running, and check that the host name "
-                    "and port settings are correct.")
+                                    "The connection was refused by the peer. Make sure the "
+                                    "fortune server is running, and check that the host name "
+                                    "and port settings are correct.")
         else:
             reason = self._tcp_socket.errorString()
             QMessageBox.information(self, "Fortune Client",
-                    f"The following error occurred: {reason}.")
+                                    f"The following error occurred: {reason}.")
 
         self._get_fortune_button.setEnabled(True)
 
     def enable_get_fortune_button(self):
-        self._get_fortune_button.setEnabled(bool(self._host_line_edit.text() and
-                self._port_line_edit.text()))
+        self._get_fortune_button.setEnabled(bool(self._host_line_edit.text()
+                                                 and self._port_line_edit.text()))
 
 
 if __name__ == '__main__':
index d84c9dcfd603e3662dade4528ab73205f6e77d2a..a94a49f4204fe15e5f3313acb7757a2819181996 100644 (file)
@@ -27,21 +27,21 @@ class Server(QDialog):
         if not self._tcp_server.listen():
             reason = self._tcp_server.errorString()
             QMessageBox.critical(self, "Fortune Server",
-                    f"Unable to start the server: {reason}.")
+                                 f"Unable to start the server: {reason}.")
             self.close()
             return
         port = self._tcp_server.serverPort()
         status_label.setText(f"The server is running on port {port}.\nRun the "
-                "Fortune Client example now.")
+                             "Fortune Client example now.")
 
         self.fortunes = (
-                "You've been leading a dog's life. Stay off the furniture.",
-                "You've got to think about tomorrow.",
-                "You will be surprised by a loud noise.",
-                "You will feel hungry again in another hour.",
-                "You might have mail.",
-                "You cannot kill time without injuring eternity.",
-                "Computers are not intelligent. They only think they are.")
+            "You've been leading a dog's life. Stay off the furniture.",
+            "You've got to think about tomorrow.",
+            "You will be surprised by a loud noise.",
+            "You will feel hungry again in another hour.",
+            "You might have mail.",
+            "You cannot kill time without injuring eternity.",
+            "Computers are not intelligent. They only think they are.")
 
         quit_button.clicked.connect(self.close)
         self._tcp_server.newConnection.connect(self.send_fortune)
index 3cb9f757d1304626b7b35f099f344f2fc423169b..d3fc67c05d52d148635e28d65fad6d8a2c1d354a 100644 (file)
@@ -120,12 +120,11 @@ class GSuggestCompletion(QObject):
 
     @Slot(QNetworkReply)
     def handle_network_data(self, network_reply: QNetworkReply):
-        url = network_reply.url()
         if network_reply.error() == QNetworkReply.NoError:
             choices: List[str] = []
 
             response: QByteArray = network_reply.readAll()
-            xml = QXmlStreamReader(response)
+            xml = QXmlStreamReader(str(response))
             while not xml.atEnd():
                 xml.readNext()
                 if xml.tokenType() == QXmlStreamReader.StartElement:
index 60900708ed948a629bffc405df53679bff59ef31..c75e2bc572df15040bcaf5e66b904e5a2fe011b3 100644 (file)
@@ -7,11 +7,11 @@
 import random
 
 from PySide6.QtCore import (Signal, QByteArray, QDataStream, QIODevice,
-        QThread, Qt)
+                            QThread, Qt)
 from PySide6.QtWidgets import (QApplication, QDialog, QHBoxLayout, QLabel,
-        QMessageBox, QPushButton, QVBoxLayout)
+                               QMessageBox, QPushButton, QVBoxLayout)
 from PySide6.QtNetwork import (QHostAddress, QNetworkInterface, QTcpServer,
-        QTcpSocket)
+                               QTcpSocket)
 
 
 class FortuneThread(QThread):
@@ -75,7 +75,7 @@ class Dialog(QDialog):
         if not self.server.listen():
             reason = self.server.errorString()
             QMessageBox.critical(self, "Threaded Fortune Server",
-                    f"Unable to start the server: {reason}.")
+                                 f"Unable to start the server: {reason}.")
             self.close()
             return
 
@@ -89,7 +89,7 @@ class Dialog(QDialog):
         port = self.server.serverPort()
 
         status_label.setText(f"The server is running on\n\nIP: {ip_address}\nport: {port}\n\n"
-                "Run the Fortune Client example now.")
+                             "Run the Fortune Client example now.")
 
         quit_button.clicked.connect(self.close)
 
index de81ceb1986438e9e7f5777dc0c13f0cb387b10f..fd6f9bc97ad17cc21a2f3d28054f4e484ee5f9c4 100644 (file)
@@ -51,7 +51,7 @@ class RedditModel(QAbstractTableModel):
         document = QJsonDocument.fromJson(json)
         root_object = document.object()
         kind = root_object["kind"]
-        assert(kind == "Listing")
+        assert kind == "Listing"
         data_object = root_object["data"]
         children_array = data_object["children"]
         if not children_array:
index f889a9366eedf320071f3331062b7006540d4daf..ed4c3d2c7f76015ecc2ed4597dbb1acaf6756409 100644 (file)
@@ -19,6 +19,7 @@ NEW_URL = "https://oauth.reddit.com/new"
 HOT_URL = "https://oauth.reddit.com/hot"
 LIVE_THREADS_URL = "https://oauth.reddit.com/live/XXXX/about.json"
 
+
 class RedditWrapper(QObject):
 
     authenticated = Signal()
@@ -78,7 +79,7 @@ class RedditWrapper(QObject):
 
         json = reply.readAll()
         document = QJsonDocument.fromJson(json)
-        assert(document.isObject())
+        assert document.isObject()
         root_object = document.object()
         data_object = root_object["data"]
         websocketUrl = QUrl(data_object["websocket_url"])
index 37c5f5d4371c635e68e277a1346fccd0b2a2bff1..311d5b76511d4bca0d8e74163745c00eb382fc7a 100644 (file)
@@ -15,15 +15,14 @@ from PySide6.QtGui import (QMatrix4x4, QOpenGLContext, QSurfaceFormat, QWindow)
 from PySide6.QtOpenGL import (QOpenGLBuffer, QOpenGLShader,
                               QOpenGLShaderProgram, QOpenGLVertexArrayObject)
 from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMessageBox, QPlainTextEdit,
-    QWidget)
+                               QWidget)
 from PySide6.support import VoidPtr
 try:
     from OpenGL import GL
 except ImportError:
     app = QApplication(sys.argv)
     message_box = QMessageBox(QMessageBox.Critical, "ContextInfo",
-                             "PyOpenGL must be installed to run this example.",
-                             QMessageBox.Close)
+                              "PyOpenGL must be installed to run this example.", QMessageBox.Close)
     message_box.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate")
     message_box.exec()
     sys.exit(1)
@@ -74,7 +73,10 @@ colors = numpy.array([1, 0, 0, 0, 1, 0, 0, 0, 1], dtype=numpy.float32)
 
 
 def print_surface_format(surface_format):
-    profile_name = 'core' if surface_format.profile() == QSurfaceFormat.CoreProfile else 'compatibility'
+    if surface_format.profile() == QSurfaceFormat.CoreProfile:
+        profile_name = 'core'
+    else:
+        profile_name = 'compatibility'
     major = surface_format.majorVersion()
     minor = surface_format.minorVersion()
     return f"{profile_name} version {major}.{minor}"
@@ -104,11 +106,13 @@ class RenderWindow(QWindow):
         # concept 3.2+ has. This may still fail since version 150 (3.2) is
         # specified in the sources but it's worth a try.
         if (fmt.renderableType() == QSurfaceFormat.OpenGL and fmt.majorVersion() == 3
-            and fmt.minorVersion() <= 1):
+                and fmt.minorVersion() <= 1):
             use_new_style_shader = not fmt.testOption(QSurfaceFormat.DeprecatedFunctions)
 
         vertex_shader = vertex_shader_source if use_new_style_shader else vertex_shader_source_110
-        fragment_shader = fragment_shader_source if use_new_style_shader else fragment_shader_source_110
+        fragment_shader = (fragment_shader_source
+                           if use_new_style_shader
+                           else fragment_shader_source_110)
         if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertex_shader):
             log = self.program.log()
             raise Exception("Vertex shader could not be added: {log} ({vertexShader})")
index d19053a26a225bbfdc05badfd7e230dc48815ad1..69b9b66fee40929e95b9ba29f586e9dafb1e718a 100644 (file)
@@ -16,7 +16,7 @@ class MainWindow(QMainWindow):
         menuWindow.addAction("Add new", QKeySequence(Qt.CTRL | Qt.Key_N),
                              self.onAddNew)
         menuWindow.addAction("Quit", QKeySequence(Qt.CTRL | Qt.Key_Q),
-                             qApp.closeAllWindows)
+                             qApp.closeAllWindows)  # noqa: F821
 
         self.onAddNew()
 
index c7b9946e0e3a2f7ffbf262f9deab8e2ad6a4c72c..ad61d2f97bcd561a0481b33c23684c596e31ed9b 100644 (file)
@@ -3,8 +3,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
 from PySide6.QtCore import Slot, Qt
-from PySide6.QtGui import QGuiApplication
-from PySide6.QtWidgets import (QApplication, QHBoxLayout, QMainWindow,
+from PySide6.QtWidgets import (QHBoxLayout, QMainWindow,
                                QMessageBox, QPushButton, QSlider,
                                QVBoxLayout, QWidget)
 
@@ -12,7 +11,7 @@ from glwidget import GLWidget
 
 
 def _main_window():
-    for t in qApp.topLevelWidgets():
+    for t in qApp.topLevelWidgets():  # noqa: F821
         if isinstance(t, QMainWindow):
             return t
     return None
index 79f7761a258ba86e4b64d198d46df0c7d58e2a08..87c1164b7aaaf8362bdb76b4ef8f20f94c81088e 100644 (file)
@@ -24,7 +24,7 @@ except ImportError:
     messageBox.exec()
     sys.exit(1)
 
-import textures_rc
+import textures_rc  # noqa: F401
 
 
 class GLWidget(QOpenGLWidget):
@@ -32,12 +32,12 @@ class GLWidget(QOpenGLWidget):
     refCount = 0
 
     coords = (
-        ( ( +1, -1, -1 ), ( -1, -1, -1 ), ( -1, +1, -1 ), ( +1, +1, -1 ) ),
-        ( ( +1, +1, -1 ), ( -1, +1, -1 ), ( -1, +1, +1 ), ( +1, +1, +1 ) ),
-        ( ( +1, -1, +1 ), ( +1, -1, -1 ), ( +1, +1, -1 ), ( +1, +1, +1 ) ),
-        ( ( -1, -1, -1 ), ( -1, -1, +1 ), ( -1, +1, +1 ), ( -1, +1, -1 ) ),
-        ( ( +1, -1, +1 ), ( -1, -1, +1 ), ( -1, -1, -1 ), ( +1, -1, -1 ) ),
-        ( ( -1, -1, +1 ), ( +1, -1, +1 ), ( +1, +1, +1 ), ( -1, +1, +1 ) )
+        ((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)),
+        ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)),
+        ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)),
+        ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)),
+        ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)),
+        ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1))
     )
 
     clicked = Signal()
@@ -186,7 +186,7 @@ class Window(QWidget):
                 mainLayout.addWidget(glw, i, j)
 
                 glw.clicked.connect(self.setCurrentGlWidget)
-                qApp.lastWindowClosed.connect(glw.freeGLResources)
+                qApp.lastWindowClosed.connect(glw.freeGLResources)  # noqa: F821
 
         self.currentGlWidget = self.glWidgets[0][0]
 
index fbed79423f4397e8fbb099844e6a1e7f39c03f5c..81ec63cbb1e6ab63c5a1dcf9be62eb450c965a6d 100644 (file)
@@ -120,7 +120,7 @@ class Renderer(QObject, QOpenGLFunctions):
             if self._exiting:
                 return
 
-            assert(ctx.thread() == QThread.currentThread())
+            assert ctx.thread() == QThread.currentThread()
 
             # Make the context (and an offscreen surface) current for self thread.
             # The QOpenGLWidget's fbo is bound in the context.
index 9d6ea68c8d5c13dca3c51d93ca5119547fbfb47e..ee7b882c22b1ccb516cece4004c16eb66a67cca0 100644 (file)
@@ -11,7 +11,7 @@ from PySide6.QtQml import QQmlApplicationEngine
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtCore import QCoreApplication, QUrl
 
-import rc_viewer
+import rc_viewer  # noqa: F401
 
 """PySide6 port of the pdf/pdfviewer example from Qt v6.x"""
 
index 60bb24f539736a4b2b2b5ec463da5d7f38feab1d..2b45b23f07e33e571dc417f89201f89c48096477 100644 (file)
@@ -1,6 +1,8 @@
 QAbstractListModel in QML
 =========================
 
+.. tags:: Android
+
 This example shows how to add, remove and move items inside a QML
 ListView, but showing and editing the data via roles using a
 QAbstractListModel from Python.
index 00b3ae2b1b660b238a5ff5b858584304c2c18ac8..5240a9de059559580d6372fe0d1c3ecd105cfa18 100644 (file)
@@ -8,7 +8,7 @@ from PySide6.QtCore import QUrl
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine
 
-from model import BaseModel
+from model import BaseModel  # noqa: F401
 
 if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
index 59149787292fbe6441f9df62a6d01ecfe399a24f..02a1e5717d7b7786c38b46ffd0fee4d5fef21d30 100644 (file)
@@ -2,8 +2,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
 
-from PySide6.QtCore import (QAbstractListModel, QByteArray, QModelIndex, Qt,
-                            Slot)
+from PySide6.QtCore import QAbstractListModel, QByteArray, QModelIndex, Qt, Slot
 from PySide6.QtGui import QColor
 from PySide6.QtQml import QmlElement
 
@@ -104,18 +103,18 @@ class BaseModel(QAbstractListModel):
         self.beginMoveRows(QModelIndex(), sourceRow, sourceRow + count, QModelIndex(), end)
 
         # start database work
-        pops = self.db[sourceRow : sourceRow + count + 1]
+        pops = self.db[sourceRow: sourceRow + count + 1]
         if sourceRow > dstChild:
             self.db = (
                 self.db[:dstChild]
                 + pops
                 + self.db[dstChild:sourceRow]
-                + self.db[sourceRow + count + 1 :]
+                + self.db[sourceRow + count + 1:]
             )
         else:
             start = self.db[:sourceRow]
-            middle = self.db[dstChild : dstChild + 1]
-            endlist = self.db[dstChild + count + 1 :]
+            middle = self.db[dstChild: dstChild + 1]
+            endlist = self.db[dstChild + count + 1:]
             self.db = start + middle + pops + endlist
         # end database work
 
@@ -136,7 +135,7 @@ class BaseModel(QAbstractListModel):
         self.beginRemoveRows(QModelIndex(), row, row + count)
 
         # start database work
-        self.db = self.db[:row] + self.db[row + count + 1 :]
+        self.db = self.db[:row] + self.db[row + count + 1:]
         # end database work
 
         self.endRemoveRows()
index faf13f42fba5e1514d7428cfce909bb5eb6ee7e4..ec703dbf3c2a620c90d44cbbd755d3f663f56abf 100644 (file)
@@ -9,7 +9,7 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Person
+from person import Person  # noqa: F401
 
 
 if __name__ == '__main__':
index fafb9d581eb7e01d1c3dba64df825b4faa7d5698..526eae7142a233ba62c993a14c3bd353b7ecb31c 100644 (file)
@@ -32,4 +32,3 @@ class Person(QObject):
     @shoe_size.setter
     def shoe_size(self, s):
         self._shoe_size = s
-
index dc07a2a5f3291a1746268f46b5cdf1b30e12b276..560db6602db57c15103544e29b38015837c0acad 100644 (file)
@@ -1,7 +1,8 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the  qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
+"""PySide6 port of the
+   qml/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project example from Qt v6.x"""
 
 from pathlib import Path
 import sys
@@ -9,8 +10,8 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Person
-from birthdayparty import BirthdayParty
+from person import Person  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 app = QCoreApplication(sys.argv)
index ef4b5ac5a86d574f29451cfdd01676f07d907b39..cc77e2b406fdfd913609395406ae76489621f682 100644 (file)
@@ -1,17 +1,18 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example from Qt v6.x"""
+"""PySide6 port of the
+   qml/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion example
+   from Qt v6.x"""
 
 from pathlib import Path
-import os
 import sys
 
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 app = QCoreApplication(sys.argv)
index f469538b61261b2c1aece3374730b6423fd0f559..020974c9b3f6a5cb66e09b271c212648ea66d05c 100644 (file)
@@ -1,7 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example from Qt v6.x"""
+"""PySide6 port of the
+   qml/examples/qml/tutorials/extending-qml-advanced/default advanced3-Default-properties example
+   from Qt v6.x"""
 
 from pathlib import Path
 import sys
@@ -9,8 +11,8 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 app = QCoreApplication(sys.argv)
index 3cdddbd06f4c1ea504d8b23c48379fbd10ba8bd3..9757b6daa815753077b01550c4c01e43336b0b5c 100644 (file)
@@ -1,7 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example from Qt v6.x"""
+"""PySide6 port of the
+   qml/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties example
+   from Qt v6.x"""
 
 from pathlib import Path
 import sys
@@ -9,8 +11,8 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 if __name__ == '__main__':
index bb607ac769e8e8ac8712abe509e817df6f6b0b99..9a92afeb576cd1e2e0a47db327ecf6e8fef7dda0 100644 (file)
@@ -1,7 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example from Qt v6.x"""
+"""PySide6 port of the
+   qml/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties example
+   from Qt v6.x"""
 
 from pathlib import Path
 import sys
@@ -9,8 +11,8 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
 
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
+from person import Boy, Girl  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 app = QCoreApplication(sys.argv)
index b763a456ae03bf259691f16c55c15f77d9ce4ca6..ea412a547bbbb066c867eeef0a2a272a640e2057 100644 (file)
@@ -1,7 +1,9 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example from Qt v6.x"""
+"""PySide6 port of the
+   qml/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source example
+   from Qt v6.x"""
 
 from pathlib import Path
 import sys
@@ -9,9 +11,9 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
 
-from person import Boy, Girl
+from person import Boy, Girl  # noqa: F401
 from birthdayparty import BirthdayParty
-from happybirthdaysong import HappyBirthdaySong
+from happybirthdaysong import HappyBirthdaySong  # noqa: F401
 
 
 app = QCoreApplication(sys.argv)
index 738bab086eba02b2cdcefe0de09b7518da806ac6..64929a80759f42eed09f62e5040debe6ca4ad60a 100644 (file)
@@ -9,9 +9,9 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine, qmlAttachedPropertiesObject
 
-from person import Boy, Girl
-from birthdayparty import BirthdayParty
-from happybirthdaysong import HappyBirthdaySong
+from person import Boy, Girl  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
+from happybirthdaysong import HappyBirthdaySong  # noqa: F401
 
 
 if __name__ == "__main__":
index 5f51ebe1fb55a6cd14af57b2e432db17dde8ed65..fb656f2667d8a74cca29b7a3ffba5e79eacaef43 100644 (file)
@@ -9,8 +9,8 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Person
-from birthdayparty import BirthdayParty
+from person import Person  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 app = QCoreApplication(sys.argv)
index 4797e4c7c834c7854d40199f817cce6e9b7c7cea..11757d5f3a40f8b8ea13d62bd47d282116151a10 100644 (file)
@@ -9,8 +9,8 @@ import sys
 from PySide6.QtCore import QCoreApplication
 from PySide6.QtQml import QQmlComponent, QQmlEngine
 
-from person import Person
-from birthdayparty import BirthdayParty
+from person import Person  # noqa: F401
+from birthdayparty import BirthdayParty  # noqa: F401
 
 
 if __name__ == '__main__':
index fafb9d581eb7e01d1c3dba64df825b4faa7d5698..526eae7142a233ba62c993a14c3bd353b7ecb31c 100644 (file)
@@ -32,4 +32,3 @@ class Person(QObject):
     @shoe_size.setter
     def shoe_size(self, s):
         self._shoe_size = s
-
index c70d7fc4267ddf3286209b7a588cac3c22c41571..659850f38a48ea042cb72aee3f4b51b1cd4b8e24 100644 (file)
@@ -1,7 +1,8 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the qml/tutorials/extending-qml/chapter4-customPropertyTypes example from Qt v5.x"""
+"""PySide6 port of the qml/tutorials/extending-qml/chapter4-customPropertyTypes example
+   from Qt v5.x"""
 
 import os
 from pathlib import Path
index 236c487f9ace803d7432bd0b4b376a98b3555d56..98952cef18878a61379481e7b84a25586d95a909 100644 (file)
@@ -54,7 +54,8 @@ class PieSlice (QQuickPaintedItem):
         pen = QPen(self._color, 2)
         painter.setPen(pen)
         painter.setRenderHints(QPainter.Antialiasing, True)
-        painter.drawPie(self.boundingRect().adjusted(1, 1, -1, -1), self._fromAngle * 16, self._angleSpan * 16)
+        painter.drawPie(
+            self.boundingRect().adjusted(1, 1, -1, -1), self._fromAngle * 16, self._angleSpan * 16)
 
 
 @QmlElement
index 6a7e80ccbc8bb24cc92e4aa3312f611bf3a1dadb..3ab8bcc08c85ab4dc2e907a03359401ce2d92084 100644 (file)
@@ -12,6 +12,7 @@ from pieslice import PieSlice
 QML_IMPORT_NAME = "Charts"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 class PieChart(QQuickItem):
     def __init__(self, parent=None):
index 67242a967f762dcbedf12c9c5f1e4e8253cd9c33..6f82f1f10930207264758b7678e29f4207983ec2 100644 (file)
@@ -11,6 +11,7 @@ from PySide6.QtQml import QmlElement
 QML_IMPORT_NAME = "Charts"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 class PieSlice(QQuickPaintedItem):
     def __init__(self, parent=None):
index 11b476d09b64851e6b910141e0948c9ded7d565b..06a1b27b09ad639aa0cd8ce48204e989917ec495 100644 (file)
@@ -1,6 +1,8 @@
 Using Model Example
 ===================
 
+.. tags:: Android
+
 A Python application that demonstrates how to use a :ref:`QAbstractListModel`
 with QML.
 
index 6f8ea5a21d2a7b0a3ae215402eaddbf8e29be855..008a1b94b372a4b665256e2f783e19fae667a0ab 100644 (file)
@@ -2,24 +2,37 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
 import os
+from dataclasses import dataclass
 from pathlib import Path
 import sys
 from PySide6.QtCore import QAbstractListModel, Qt, QUrl, QByteArray
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQuick import QQuickView
-from PySide6.QtQml import qmlRegisterSingletonType
+from PySide6.QtQml import QmlElement, QmlSingleton
 
 
+QML_IMPORT_NAME = "PersonModel"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@dataclass
+class Person:
+    name: str
+    myrole: str
+
+
+@QmlElement
+@QmlSingleton
 class PersonModel (QAbstractListModel):
     MyRole = Qt.UserRole + 1
 
-    def __init__(self, parent=None):
-        QAbstractListModel.__init__(self, parent)
-        self._data = []
+    def __init__(self, data, parent=None):
+        super().__init__(parent)
+        self._data = data
 
     def roleNames(self):
         roles = {
-            PersonModel.MyRole: QByteArray(b'modelData'),
+            PersonModel.MyRole: QByteArray(b'myrole'),
             Qt.DisplayRole: QByteArray(b'display')
         }
         return roles
@@ -29,26 +42,18 @@ class PersonModel (QAbstractListModel):
 
     def data(self, index, role):
         d = self._data[index.row()]
-
         if role == Qt.DisplayRole:
-            return d['name']
-        elif role == Qt.DecorationRole:
+            return d.name
+        if role == Qt.DecorationRole:
             return Qt.black
-        elif role == PersonModel.MyRole:
-            return d['myrole']
+        if role == PersonModel.MyRole:
+            return d.myrole
         return None
 
-    def populate(self, data=None):
-        for item in data:
-            self._data.append(item)
-
-
-def model_callback(engine):
-    my_model = PersonModel()
-    data = [{'name': 'Qt', 'myrole': 'role1'},
-            {'name': 'PySide', 'myrole': 'role2'}]
-    my_model.populate(data)
-    return my_model
+    @staticmethod
+    def create(engine):
+        data = [Person('Qt', 'myrole'), Person('PySide', 'role2')]
+        return PersonModel(data)
 
 
 if __name__ == '__main__':
@@ -56,7 +61,6 @@ if __name__ == '__main__':
     view = QQuickView()
     view.setResizeMode(QQuickView.SizeRootObjectToView)
 
-    qmlRegisterSingletonType(PersonModel, "PersonModel", 1, 0, "MyModel", model_callback)
     qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
     view.setSource(QUrl.fromLocalFile(qml_file))
     if view.status() == QQuickView.Error:
index c5aa7e0fc6edf7d8e7acf0e318eeff96da0771c6..e8b1fb2fba56afe215cf04a928e6ece85cf26809 100644 (file)
@@ -8,21 +8,13 @@ ListView {
     width: 100
     height: 100
     anchors.fill: parent
-    model: MyModel
+    model: PersonModel
     delegate: Component {
         Rectangle {
             height: 25
             width: 100
             Text {
-                function displayText() {
-                    var result = ""
-                    if (typeof display !== "undefined")
-                        result = display + ": "
-                    result += modelData
-                    return result
-                }
-
-                text: displayText()
+                text: display + ": " + myrole
             }
         }
     }
index d71ee61df2c962fcc51971cf112eaf76b3d40de2..a4af62706f0de399ab3f59cdb254272f5d54f434 100644 (file)
@@ -1,6 +1,8 @@
 Object List Model Example
 =========================
 
+.. tags:: Android
+
 A list of QObject values can also be used as a model.
 A list[QObject,] provides the properties of the objects in the list as roles.
 
index 0843ae4805cff997621031a965410e8142ecfcfc..968761e5cc99582107d468b32671009cedc06145 100644 (file)
@@ -9,6 +9,7 @@ from PySide6.QtQuick import QQuickView
 
 # This example illustrates exposing a list of QObjects as a model in QML
 
+
 class DataObject(QObject):
 
     nameChanged = Signal()
@@ -25,7 +26,7 @@ class DataObject(QObject):
     def setName(self, name):
         if name != self._name:
             self._name = name
-            nameChanged.emit()
+            self.nameChanged.emit()
 
     def color(self):
         return self._color
@@ -33,8 +34,7 @@ class DataObject(QObject):
     def setColor(self, color):
         if color != self._color:
             self._color = color
-            colorChanged.emit()
-
+            self.colorChanged.emit()
 
     name = Property(str, name, setName, notify=nameChanged)
     color = Property(str, color, setColor, notify=colorChanged)
index 4c00ed130a9bfd0b3c32cdb081d3dbd09fd460aa..ce11674b47f5b8f8b7fb345248b9c1a9fcbd7bd6 100644 (file)
@@ -1,6 +1,8 @@
 String List Model Example
 =========================
 
+.. tags:: Android
+
 A model may be a simple 'list',
 which provides the contents of the list via the modelData role.
 
index a7a1807bbd8fcdd494ab75bfdf17c43d3e36999b..3982b1ffc4e0d9c19f1406b763a4b2e3a03f18dd 100644 (file)
@@ -15,7 +15,7 @@ if __name__ == '__main__':
     dataList = ["Item 1", "Item 2", "Item 3", "Item 4"]
 
     view = QQuickView()
-    view.setInitialProperties({"model": dataList })
+    view.setInitialProperties({"model": dataList})
 
     qml_file = Path(__file__).parent / "view.qml"
     view.setSource(QUrl.fromLocalFile(qml_file))
index 6f12355ca33edf6f8d32aa0ce1edf3a8289d97b3..5c3e8935b57dabff849a107bbe00890b3d26df16 100644 (file)
@@ -1,6 +1,8 @@
 Scene Graph Painted Item Example
 ================================
 
+.. tags:: Android
+
 Shows how to implement QPainter-based custom scenegraph items.
 
 The Painted Item example shows how to use the QML Scene Graph framework to
index ae56988aa5c5a252c16c5cca19773553cb218d66..69e7321f9da31d87df41a3549e27d27f577783ca 100644 (file)
@@ -45,27 +45,27 @@ VERTEXES = numpy.array([-0.5, 0.5, 0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5,
                         -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5,
                         -0.5, -0.5, 0.5, -0.5, 0.5, -0.5, -0.5, 0.5, 0.5,
 
-                        0.5, 0.5,  -0.5, -0.5, 0.5,  0.5,  -0.5,  0.5,  -0.5,
-                        -0.5,  0.5,  0.5,  0.5,  0.5,  -0.5, 0.5, 0.5,  0.5,
-                        -0.5,  -0.5, -0.5, -0.5, -0.5, 0.5,  0.5, -0.5, -0.5,
-                        0.5, -0.5, 0.5,  0.5,  -0.5, -0.5, -0.5,  -0.5, 0.5],
+                        0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, 0.5, -0.5,
+                        -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5,
+                        -0.5, -0.5, -0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5,
+                        0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5, 0.5],
                        dtype=numpy.float32)
 
 
-TEX_COORDS = numpy.array([0.0, 0.0,  1.0, 1.0,  1.0, 0.0,
-                          1.0, 1.0,  0.0, 0.0,  0.0, 1.0,
-                          1.0, 1.0,  1.0, 0.0,  0.0, 1.0,
-                          0.0, 0.0,  0.0, 1.0,  1.0, 0.0,
+TEX_COORDS = numpy.array([0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
+                          1.0, 1.0, 0.0, 0.0, 0.0, 1.0,
+                          1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
+                          0.0, 0.0, 0.0, 1.0, 1.0, 0.0,
 
-                          1.0, 1.0,  1.0, 0.0,  0.0, 1.0,
-                          0.0, 0.0,  0.0, 1.0,  1.0, 0.0,
-                          0.0, 0.0,  1.0, 1.0,  1.0, 0.0,
-                          1.0, 1.0,  0.0, 0.0,  0.0, 1.0,
+                          1.0, 1.0, 1.0, 0.0, 0.0, 1.0,
+                          0.0, 0.0, 0.0, 1.0, 1.0, 0.0,
+                          0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
+                          1.0, 1.0, 0.0, 0.0, 0.0, 1.0,
 
-                          0.0, 1.0,  1.0, 0.0,  1.0, 1.0,
-                          1.0, 0.0,  0.0, 1.0,  0.0, 0.0,
-                          1.0, 0.0,  1.0, 1.0,  0.0, 0.0,
-                          0.0, 1.0,  0.0, 0.0,  1.0, 1.0], dtype=numpy.float32)
+                          0.0, 1.0, 1.0, 0.0, 1.0, 1.0,
+                          1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
+                          1.0, 0.0, 1.0, 1.0, 0.0, 0.0,
+                          0.0, 1.0, 0.0, 0.0, 1.0, 1.0], dtype=numpy.float32)
 
 
 class CubeRenderer():
@@ -113,7 +113,6 @@ class CubeRenderer():
 
         self.m_vao = QOpenGLVertexArrayObject()
         self.m_vao.create()
-        vaoBinder = QOpenGLVertexArrayObject.Binder(self.m_vao)
 
         self.m_vbo = QOpenGLBuffer()
         self.m_vbo.create()
@@ -166,7 +165,6 @@ class CubeRenderer():
             f.glEnable(GL_DEPTH_TEST)
 
             self.m_program.bind()
-            vaoBinder = QOpenGLVertexArrayObject.Binder(self.m_vao)
             # If VAOs are not supported, set the vertex attributes every time.
             if not self.m_vao.isCreated():
                 self.setupVertexAttribs()
index 0516476966d365272459541971c062bfff76d7dd..6f1e61f943ed7d7c46dc704551fffe38b7509106 100644 (file)
@@ -247,7 +247,7 @@ class WindowSingleThreaded(QWindow):
         # If self is a resize after the scene is up and running, recreate the
         # texture and the Quick item and scene.
         if (self.texture_id()
-            and self.m_textureSize != self.size() * self.devicePixelRatio()):
+                and self.m_textureSize != self.size() * self.devicePixelRatio()):
             self.resizeTexture()
 
     @Slot()
index a79d1bed86bdc1c11cb5407b7f9237170a017e62..0e24877bda08e4a3485964cfbea9477353d13b77 100644 (file)
@@ -8,7 +8,7 @@ from PySide6.QtCore import QUrl
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQuick import QQuickView, QQuickWindow, QSGRendererInterface
 
-from squircle import Squircle
+from squircle import Squircle  # noqa: F401
 
 if __name__ == "__main__":
     app = QGuiApplication(sys.argv)
index 549e4251e7f0ea51c2f76442ea984b95e77b50b7..62ba6a5e9ee4f1d6a3ac166f75108e7e53d68c98 100644 (file)
@@ -11,12 +11,12 @@ from PySide6.QtQml import QQmlComponent, QQmlEngine
 from PySide6.QtQuick import QQuickWindow
 from PySide6.QtQuickControls2 import QQuickStyle
 
-import rc_window
+import rc_window  # noqa: F401
 
 # Append the parent directory of this file so that Python can find and
 # import from the "shared" sibling directory.
 sys.path.append(os.fspath(Path(__file__).parent.parent))
-from shared import shared_rc
+from shared import shared_rc  # noqa: F401, E402
 
 
 if __name__ == "__main__":
index ff1ce384e57aff22bd384a8edf8dfb77419ef4ae..148330d91451108db98f084b7644c4b853bd5231 100644 (file)
@@ -11,9 +11,9 @@ from PySide6.QtQml import QQmlApplicationEngine
 from PySide6.QtQuick3D import QQuick3D
 
 # Imports to trigger the resources and registration of QML elements
-import resources_rc
-from examplepoint import ExamplePointGeometry
-from exampletriangle import ExampleTriangleGeometry
+import resources_rc  # noqa: F401
+from examplepoint import ExamplePointGeometry  # noqa: F401
+from exampletriangle import ExampleTriangleGeometry  # noqa: F401
 
 if __name__ == "__main__":
     os.environ["QT_QUICK_CONTROLS_STYLE"] = "Basic"
index b6dd47de8e9b4148643897df4b7575842c4a8758..610c08a27471d6db1fa547586aca7b0bcfb3a000 100644 (file)
@@ -82,7 +82,7 @@ ApplicationWindow {
                                 );
                 }
 
-                function generateTextureData() : ArrayBuffer {
+                function generateTextureData() {
                     let dataBuffer = new ArrayBuffer(width * height * 4)
                     let data = new Uint8Array(dataBuffer)
 
index 1511905cb255152d90fafc9dd9eb7863ffc8e06d..a577f7ebd52f6bd8f275c3619feb1b8de7919bed 100644 (file)
@@ -103,4 +103,3 @@ class GradientTexture(QQuick3DTextureData):
         output.setBlueF(color1.blueF() + (value * (color2.blueF() - color1.blueF())))
 
         return output
-
index 971b0ecb434540278f7708bb48a90f0df551f5cf..a732cc01587e31c2e138c10a6852844b40eb345d 100644 (file)
@@ -4,8 +4,11 @@
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtQml import QQmlApplicationEngine
 
-from gradienttexture import GradientTexture
+from gradienttexture import GradientTexture  # noqa: F401
 
+from pathlib import Path
+
+import os
 import sys
 
 if __name__ == "__main__":
diff --git a/examples/quickcontrols/contactslist/Contact/ContactDelegate.ui.qml b/examples/quickcontrols/contactslist/Contact/ContactDelegate.ui.qml
new file mode 100644 (file)
index 0000000..affcccc
--- /dev/null
@@ -0,0 +1,82 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+ItemDelegate {
+    id: delegate
+
+    checkable: true
+
+    contentItem: ColumnLayout {
+        spacing: 10
+
+        Label {
+            text: fullName
+            font.bold: true
+            elide: Text.ElideRight
+            Layout.fillWidth: true
+        }
+
+        GridLayout {
+            id: grid
+            visible: false
+
+            columns: 2
+            rowSpacing: 10
+            columnSpacing: 10
+
+            Label {
+                text: qsTr("Address:")
+                Layout.leftMargin: 60
+            }
+
+            Label {
+                text: address
+                font.bold: true
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+            }
+
+            Label {
+                text: qsTr("City:")
+                Layout.leftMargin: 60
+            }
+
+            Label {
+                text: city
+                font.bold: true
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+            }
+
+            Label {
+                text: qsTr("Number:")
+                Layout.leftMargin: 60
+            }
+
+            Label {
+                text: number
+                font.bold: true
+                elide: Text.ElideRight
+                Layout.fillWidth: true
+            }
+        }
+    }
+
+    states: [
+        State {
+            name: "expanded"
+            when: delegate.checked
+
+            PropertyChanges {
+                // TODO: When Qt Design Studio supports generalized grouped properties, change to:
+                //       grid.visible: true
+                target: grid
+                visible: true
+            }
+        }
+    ]
+}
diff --git a/examples/quickcontrols/contactslist/Contact/ContactDialog.qml b/examples/quickcontrols/contactslist/Contact/ContactDialog.qml
new file mode 100644 (file)
index 0000000..d906f00
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+Dialog {
+    id: dialog
+
+    signal finished(string fullName, string address, string city, string number)
+
+    function createContact() {
+        form.fullName.clear();
+        form.address.clear();
+        form.city.clear();
+        form.number.clear();
+
+        dialog.title = qsTr("Add Contact");
+        dialog.open();
+    }
+
+    function editContact(contact) {
+        form.fullName.text = contact.fullName;
+        form.address.text = contact.address;
+        form.city.text = contact.city;
+        form.number.text = contact.number;
+
+        dialog.title = qsTr("Edit Contact");
+        dialog.open();
+    }
+
+    x: parent.width / 2 - width / 2
+    y: parent.height / 2 - height / 2
+
+    focus: true
+    modal: true
+    title: qsTr("Add Contact")
+    standardButtons: Dialog.Ok | Dialog.Cancel
+
+    contentItem: ContactForm {
+        id: form
+    }
+
+    onAccepted: finished(form.fullName.text, form.address.text, form.city.text, form.number.text)
+}
diff --git a/examples/quickcontrols/contactslist/Contact/ContactForm.ui.qml b/examples/quickcontrols/contactslist/Contact/ContactForm.ui.qml
new file mode 100644 (file)
index 0000000..56c9186
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+GridLayout {
+    id: grid
+    property alias fullName: fullName
+    property alias address: address
+    property alias city: city
+    property alias number: number
+    property int minimumInputSize: 120
+    property string placeholderText: qsTr("<enter>")
+
+    rows: 4
+    columns: 2
+
+    Label {
+        text: qsTr("Full Name")
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+    }
+
+    TextField {
+        id: fullName
+        focus: true
+        Layout.fillWidth: true
+        Layout.minimumWidth: grid.minimumInputSize
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+        placeholderText: grid.placeholderText
+    }
+
+    Label {
+        text: qsTr("Address")
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+    }
+
+    TextField {
+        id: address
+        Layout.fillWidth: true
+        Layout.minimumWidth: grid.minimumInputSize
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+        placeholderText: grid.placeholderText
+    }
+
+    Label {
+        text: qsTr("City")
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+    }
+
+    TextField {
+        id: city
+        Layout.fillWidth: true
+        Layout.minimumWidth: grid.minimumInputSize
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+        placeholderText: grid.placeholderText
+    }
+
+    Label {
+        text: qsTr("Number")
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+    }
+
+    TextField {
+        id: number
+        Layout.fillWidth: true
+        Layout.minimumWidth: grid.minimumInputSize
+        Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+        placeholderText: grid.placeholderText
+    }
+}
diff --git a/examples/quickcontrols/contactslist/Contact/ContactList.qml b/examples/quickcontrols/contactslist/Contact/ContactList.qml
new file mode 100644 (file)
index 0000000..0b7af32
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ApplicationWindow {
+    id: window
+
+    property int currentContact: -1
+
+    width: 320
+    height: 480
+    visible: true
+    title: qsTr("Contact List")
+
+    ContactDialog {
+        id: contactDialog
+        onFinished: function(fullName, address, city, number) {
+            if (currentContact == -1)
+                contactView.model.append(fullName, address, city, number)
+            else
+                contactView.model.set(currentContact, fullName, address, city, number)
+        }
+    }
+
+    Menu {
+        id: contactMenu
+        x: parent.width / 2 - width / 2
+        y: parent.height / 2 - height / 2
+        modal: true
+
+        Label {
+            padding: 10
+            font.bold: true
+            width: parent.width
+            horizontalAlignment: Qt.AlignHCenter
+            text: currentContact >= 0 ? contactView.model.get(currentContact).fullName : ""
+        }
+        MenuItem {
+            text: qsTr("Edit...")
+            onTriggered: contactDialog.editContact(contactView.model.get(currentContact))
+        }
+        MenuItem {
+            text: qsTr("Remove")
+            onTriggered: contactView.model.remove(currentContact)
+        }
+    }
+
+    ContactView {
+        id: contactView
+        anchors.fill: parent
+        onPressAndHold: {
+            currentContact = index
+            contactMenu.open()
+        }
+    }
+
+    RoundButton {
+        text: qsTr("+")
+        highlighted: true
+        anchors.margins: 10
+        anchors.right: parent.right
+        anchors.bottom: parent.bottom
+        onClicked: {
+            currentContact = -1
+            contactDialog.createContact()
+        }
+    }
+}
diff --git a/examples/quickcontrols/contactslist/Contact/ContactView.ui.qml b/examples/quickcontrols/contactslist/Contact/ContactView.ui.qml
new file mode 100644 (file)
index 0000000..3b82b68
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+import Backend
+
+ListView {
+    id: listView
+
+    signal pressAndHold(int index)
+
+    width: 320
+    height: 480
+
+    focus: true
+    boundsBehavior: Flickable.StopAtBounds
+
+    section.property: "fullName"
+    section.criteria: ViewSection.FirstCharacter
+    section.delegate: SectionDelegate {
+        width: listView.width
+    }
+
+    delegate: ContactDelegate {
+        id: delegate
+        width: listView.width
+        onPressAndHold: listView.pressAndHold(index)
+    }
+
+    model: ContactModel {
+        id: contactModel
+    }
+
+    ScrollBar.vertical: ScrollBar { }
+}
diff --git a/examples/quickcontrols/contactslist/Contact/SectionDelegate.ui.qml b/examples/quickcontrols/contactslist/Contact/SectionDelegate.ui.qml
new file mode 100644 (file)
index 0000000..3a62409
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Controls
+
+ToolBar {
+    id: background
+
+    Label {
+        id: label
+        text: section
+        anchors.fill: parent
+        horizontalAlignment: Qt.AlignHCenter
+        verticalAlignment: Qt.AlignVCenter
+    }
+}
diff --git a/examples/quickcontrols/contactslist/Contact/qmldir b/examples/quickcontrols/contactslist/Contact/qmldir
new file mode 100644 (file)
index 0000000..339d45a
--- /dev/null
@@ -0,0 +1,7 @@
+module Contact
+ContactList 1.0 ContactList.qml
+ContactDialog 1.0 ContactDialog.qml
+ContactDelegate 1.0 ContactDelegate.ui.qml
+ContactForm 1.0 ContactForm.ui.qml
+ContactView 1.0 ContactView.ui.qml
+SectionDelegate 1.0 SectionDelegate.ui.qml
diff --git a/examples/quickcontrols/contactslist/contactlist.pyproject b/examples/quickcontrols/contactslist/contactlist.pyproject
new file mode 100644 (file)
index 0000000..75b0bd6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+    "files": ["main.py",
+              "contactmodel.py",
+              "Contact/ContactDialog.qml",
+              "Contact/ContactDelegate.ui.qml",
+              "Contact/ContactForm.ui.qml",
+              "Contact/ContactList.qml",
+              "Contact/ContactView.ui.qml",
+              "Contact/SectionDelegate.ui.qml"]
+}
diff --git a/examples/quickcontrols/contactslist/contactmodel.py b/examples/quickcontrols/contactslist/contactmodel.py
new file mode 100644 (file)
index 0000000..5d2746c
--- /dev/null
@@ -0,0 +1,116 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import bisect
+from dataclasses import dataclass
+from enum import IntEnum
+
+from PySide6.QtCore import (QAbstractListModel, QEnum, Qt, QModelIndex, Slot,
+                            QByteArray)
+from PySide6.QtQml import QmlElement
+
+QML_IMPORT_NAME = "Backend"
+QML_IMPORT_MAJOR_VERSION = 1
+
+
+@QmlElement
+class ContactModel(QAbstractListModel):
+
+    @QEnum
+    class ContactRole(IntEnum):
+        FullNameRole = Qt.DisplayRole
+        AddressRole = Qt.UserRole
+        CityRole = Qt.UserRole + 1
+        NumberRole = Qt.UserRole + 2
+
+    @dataclass
+    class Contact:
+        fullName: str
+        address: str
+        city: str
+        number: str
+
+    def __init__(self, parent=None) -> None:
+        super().__init__(parent)
+        self.m_contacts = []
+        self.m_contacts.append(self.Contact("Angel Hogan", "Chapel St. 368 ", "Clearwater",
+                                            "0311 1823993"))
+        self.m_contacts.append(self.Contact("Felicia Patton", "Annadale Lane 2", "Knoxville",
+                                            "0368 1244494"))
+        self.m_contacts.append(self.Contact("Grant Crawford", "Windsor Drive 34", "Riverdale",
+                                            "0351 7826892"))
+        self.m_contacts.append(self.Contact("Gretchen Little", "Sunset Drive 348", "Virginia Beach",
+                                            "0343 1234991"))
+        self.m_contacts.append(self.Contact("Geoffrey Richards", "University Lane 54", "Trussville",
+                                            "0423 2144944"))
+        self.m_contacts.append(self.Contact("Henrietta Chavez", "Via Volto San Luca 3",
+                                            "Piobesi Torinese", "0399 2826994"))
+        self.m_contacts.append(self.Contact("Harvey Chandler", "North Squaw Creek 11",
+                                            "Madisonville", "0343 1244492"))
+        self.m_contacts.append(self.Contact("Miguel Gomez", "Wild Rose Street 13", "Trussville",
+                                            "0343 9826996"))
+        self.m_contacts.append(self.Contact("Norma Rodriguez", " Glen Eagles Street  53",
+                                            "Buffalo", "0241 5826596"))
+        self.m_contacts.append(self.Contact("Shelia Ramirez", "East Miller Ave 68", "Pickerington",
+                                            "0346 4844556"))
+        self.m_contacts.append(self.Contact("Stephanie Moss", "Piazza Trieste e Trento 77",
+                                            "Roata Chiusani", "0363 0510490"))
+
+    def rowCount(self, parent=QModelIndex()):
+        return len(self.m_contacts)
+
+    def data(self, index: QModelIndex, role: int):
+        row = index.row()
+        if row < self.rowCount():
+            if role == ContactModel.ContactRole.FullNameRole:
+                return self.m_contacts[row].fullName
+            elif role == ContactModel.ContactRole.AddressRole:
+                return self.m_contacts[row].address
+            elif role == ContactModel.ContactRole.CityRole:
+                return self.m_contacts[row].city
+            elif role == ContactModel.ContactRole.NumberRole:
+                return self.m_contacts[row].number
+
+    def roleNames(self):
+        default = super().roleNames()
+        default[ContactModel.ContactRole.FullNameRole] = QByteArray(b"fullName")
+        default[ContactModel.ContactRole.AddressRole] = QByteArray(b"address")
+        default[ContactModel.ContactRole.CityRole] = QByteArray(b"city")
+        default[ContactModel.ContactRole.NumberRole] = QByteArray(b"number")
+        return default
+
+    @Slot(int)
+    def get(self, row: int):
+        contact = self.m_contacts[row]
+        return {"fullName": contact.fullName, "address": contact.address,
+                "city": contact.city, "number": contact.number}
+
+    @Slot(str, str, str, str)
+    def append(self, full_name: str, address: str, city: str, number: str):
+        contact = self.Contact(full_name, address, city, number)
+        contact_names = [contact.fullName for contact in self.m_contacts]
+        index = bisect.bisect(contact_names, contact.fullName)
+        self.beginInsertRows(QModelIndex(), index, index)
+        self.m_contacts.insert(index, contact)
+        self.endInsertRows()
+
+    @Slot(int, str, str, str, str)
+    def set(self, row: int, full_name: str, address: str, city: str, number: str):
+        if row < 0 or row >= len(self.m_contacts):
+            return
+
+        self.m_contacts[row] = self.Contact(full_name, address, city, number)
+        self.dataChanged(self.index(row, 0), self.index(row, 0),
+                         [ContactModel.ContactRole.FullNameRole,
+                          ContactModel.ContactRole.AddressRole,
+                          ContactModel.ContactRole.CityRole,
+                          ContactModel.ContactRole.NumberRole])
+
+    @Slot(int)
+    def remove(self, row):
+        if row < 0 or row >= len(self.m_contacts):
+            return
+
+        self.beginRemoveRows(QModelIndex(), row, row)
+        del self.m_contacts[row]
+        self.endRemoveRows()
diff --git a/examples/quickcontrols/contactslist/doc/contactslist.rst b/examples/quickcontrols/contactslist/doc/contactslist.rst
new file mode 100644 (file)
index 0000000..b5540e3
--- /dev/null
@@ -0,0 +1,15 @@
+Qt Quick Controls - Contact List
+================================
+
+.. tags:: Android
+
+A QML app using Qt Quick Controls and a Python class that implements a simple
+contact list. This example can also be deployed to Android using
+**pyside6-android-deploy**
+
+A PySide6 application that demonstrates the analogous example in Qt
+`ContactsList <https://doc.qt.io/qt-6.6/qtquickcontrols-contactlist-example.html>`_
+
+.. image:: qtquickcontrols-contactlist.png
+   :width: 400
+   :alt: ContactList Screenshot
diff --git a/examples/quickcontrols/contactslist/doc/qtquickcontrols-contactlist.png b/examples/quickcontrols/contactslist/doc/qtquickcontrols-contactlist.png
new file mode 100644 (file)
index 0000000..9f1c306
Binary files /dev/null and b/examples/quickcontrols/contactslist/doc/qtquickcontrols-contactlist.png differ
diff --git a/examples/quickcontrols/contactslist/main.py b/examples/quickcontrols/contactslist/main.py
new file mode 100644 (file)
index 0000000..6c934ec
--- /dev/null
@@ -0,0 +1,27 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+"""
+PySide6 port of Qt Quick Controls Contact List example from Qt v6.x
+"""
+import sys
+from pathlib import Path
+from PySide6.QtGui import QGuiApplication
+from PySide6.QtQml import QQmlApplicationEngine
+
+from contactmodel import ContactModel  # noqa: F401
+
+if __name__ == '__main__':
+    app = QGuiApplication(sys.argv)
+    app.setOrganizationName("QtProject")
+    app.setApplicationName("ContactsList")
+    engine = QQmlApplicationEngine()
+
+    engine.addImportPath(Path(__file__).parent)
+    engine.loadFromModule("Contact", "ContactList")
+
+    if not engine.rootObjects():
+        sys.exit(-1)
+
+    del engine
+    sys.exit(app.exec())
index 90579b3607ee33cb8b9b02fa39f8b9ee0b9ee12c..2bc733c377da203fdde7911d8ce371e7540895bc 100644 (file)
@@ -44,7 +44,8 @@ class FileSystemModel(QFileSystemModel):
         file = QFile(path)
 
         mime = self.db.mimeTypeForFile(QFileInfo(file))
-        if 'text' in mime.comment().lower() or any('text' in s.lower() for s in mime.parentMimeTypes()):
+        if ('text' in mime.comment().lower()
+                or any('text' in s.lower() for s in mime.parentMimeTypes())):
             if file.open(QFile.ReadOnly | QFile.Text):
                 stream = QTextStream(file).readAll()
                 return stream
index 29cd49f14b0969fa6e3db93faec2ab772061be04..acd5096f6e453823fb30039df21adb5a851c24b2 100644 (file)
@@ -1,6 +1,8 @@
 Qt Quick Controls 2 - Gallery
 =============================
 
+.. tags:: Android
+
 The gallery example is a simple application with a drawer menu that contains
 all the Qt Quick Controls 2. Each menu item opens a page that shows the
 graphical appearance of a control, allows you to interact with the control, and
index 1cdc30bab07a7380c78edf55c55e81b705aa08c9..6c2a3612ec853dfecf4e4ceebf471ae4834d9d97 100644 (file)
@@ -17,7 +17,7 @@ from PySide6.QtCore import QSettings, QUrl
 from PySide6.QtQml import QQmlApplicationEngine
 from PySide6.QtQuickControls2 import QQuickStyle
 
-import rc_gallery
+import rc_gallery  # noqa: F401
 
 if __name__ == "__main__":
     QGuiApplication.setApplicationName("Gallery")
index 77d2e6012a994d5579ae35c8b726154880082c90..97cdcc908924d80c4e3005f2aa02733872ea07fc 100644 (file)
@@ -16,4 +16,3 @@ if __name__ == "__main__":
     w = MainWindow()
     w.show()
     sys.exit(QCoreApplication.exec())
-
index e5d068e0779679ea6aabeb8d9d3303ec5f0c08e1..ae2a08f1ff5aedfd2208d022ba3fdb181c9a436e 100644 (file)
@@ -74,7 +74,7 @@ class MainWindow(QMainWindow):
         self.m_ui.actionDisconnect.triggered.connect(self.disconnect_device)
         self.m_ui.actionResetController.triggered.connect(self._reset_controller)
         self.m_ui.actionQuit.triggered.connect(self.close)
-        self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt)
+        self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt)  # noqa: F821
         self.m_ui.actionClearLog.triggered.connect(self.m_model.clear)
         self.m_ui.actionPluginDocumentation.triggered.connect(show_help)
         self.m_ui.actionDeviceInformation.triggered.connect(self._action_device_information)
@@ -109,9 +109,11 @@ class MainWindow(QMainWindow):
         else:
             self.m_model.set_queue_limit(0)
 
-        device, error_string = QCanBus.instance().createDevice(p.plugin_name, p.device_interface_name)
+        device, error_string = QCanBus.instance().createDevice(
+            p.plugin_name, p.device_interface_name)
         if not device:
-            self.m_status.setText(f"Error creating device '{p.plugin_name}', reason: '{error_string}'")
+            self.m_status.setText(
+                f"Error creating device '{p.plugin_name}', reason: '{error_string}'")
             return
 
         self.m_number_frames_written = 0
@@ -136,18 +138,22 @@ class MainWindow(QMainWindow):
             config_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.BitRateKey)
             if config_bit_rate > 0:
                 is_can_fd = bool(self.m_can_device.configurationParameter(QCanBusDevice.CanFdKey))
-                config_data_bit_rate = self.m_can_device.configurationParameter(QCanBusDevice.DataBitRateKey)
+                config_data_bit_rate = self.m_can_device.configurationParameter(
+                    QCanBusDevice.DataBitRateKey)
                 bit_rate = config_bit_rate / 1000
                 if is_can_fd and config_data_bit_rate > 0:
                     data_bit_rate = config_data_bit_rate / 1000
-                    m = f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} at {bit_rate} / {data_bit_rate} kBit/s"
+                    m = (f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} "
+                         f"at {bit_rate} / {data_bit_rate} kBit/s")
                     self.m_status.setText(m)
                 else:
-                    m = f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} at {bit_rate} kBit/s"
+                    m = (f"Plugin: {p.plugin_name}, connected to {p.device_interface_name} "
+                         f"at {bit_rate} kBit/s")
                     self.m_status.setText(m)
 
             else:
-                self.m_status.setText(f"Plugin: {p.plugin_name}, connected to {p.device_interface_name}")
+                self.m_status.setText(
+                    f"Plugin: {p.plugin_name}, connected to {p.device_interface_name}")
 
             if self.m_can_device.hasBusStatus():
                 self.m_busStatusTimer.start(2000)
index 99e4ab7c2e5a9545e430e51255a03716919f036a..6472fc473c82caa33d8290d20f6e10aabf0326a7 100644 (file)
@@ -157,8 +157,8 @@ class SendFrameBox(QGroupBox):
 
     @Slot(bool)
     def _flexible_datarate(self, value):
-        l = MAX_PAYLOAD_FD if value else MAX_PAYLOAD
-        self.m_hexStringValidator.set_max_length(l)
+        len = MAX_PAYLOAD_FD if value else MAX_PAYLOAD
+        self.m_hexStringValidator.set_max_length(len)
         self.m_ui.bitrateSwitchBox.setEnabled(value)
         if not value:
             self.m_ui.bitrateSwitchBox.setChecked(False)
index 80a336c592cdd9b53a5ab257780f51363adac85f..02f9d478b9fba6aa85def24c85d328d8f4fd7d88 100644 (file)
@@ -316,7 +316,7 @@ class MainWindow(QMainWindow):
 
         # do not go beyond 10 entries
         number_of_entries = min(int(self.ui.readSize.currentText()),
-                              10 - start_address)
+                                10 - start_address)
         return QModbusDataUnit(table, start_address, number_of_entries)
 
     def write_request(self):
@@ -327,5 +327,5 @@ class MainWindow(QMainWindow):
 
         # do not go beyond 10 entries
         number_of_entries = min(int(self.ui.writeSize.currentText()),
-                              10 - start_address)
+                                10 - start_address)
         return QModbusDataUnit(table, start_address, number_of_entries)
index c35ed28543f335d7f874a5dd78b4d11ed4707283..bdfb0fb0ea2dc582f68d78dfd1424a62afe77a5f 100644 (file)
@@ -47,7 +47,7 @@ class MainWindow(QMainWindow):
         self.m_ui.actionConfigure.triggered.connect(self.m_settings.show)
         self.m_ui.actionClear.triggered.connect(self.m_console.clear)
         self.m_ui.actionAbout.triggered.connect(self.about)
-        self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt)
+        self.m_ui.actionAboutQt.triggered.connect(qApp.aboutQt)  # noqa: F821
 
         self.m_serial.errorOccurred.connect(self.handle_error)
         self.m_serial.readyRead.connect(self.read_data)
index 07034e4a6e7786b31cef54f003d59dcefa5c4830..c9373d5b00855badd6d5dfe0142e16eb3f044041 100644 (file)
@@ -29,7 +29,7 @@ class Settings():
         self.string_data_bits = ""
         self.parity = QSerialPort.NoParity
         self.string_parity = ""
-        self.stop_bits =  QSerialPort.OneStop
+        self.stop_bits = QSerialPort.OneStop
         self.string_stop_bits = ""
         self.flow_control = QSerialPort.SoftwareControl
         self.string_flow_control = ""
@@ -52,7 +52,8 @@ class SettingsDialog(QDialog):
         self.m_ui.applyButton.clicked.connect(self.apply)
         self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(self.show_port_info)
         self.m_ui.baudRateBox.currentIndexChanged.connect(self.check_custom_baud_rate_policy)
-        self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(self.check_custom_device_path_policy)
+        self.m_ui.serialPortInfoListBox.currentIndexChanged.connect(
+            self.check_custom_device_path_policy)
 
         self.fill_ports_parameters()
         self.fill_ports_info()
index 55770e3b6bfb3a260586d6646d3ab7b31e27c400..19b6c3bbd84bda86164bcc40b743f047b18790b2 100644 (file)
@@ -149,7 +149,7 @@ class AudioWidget(QWidget):
             self._file_dialog = QFileDialog(self, "Open Audio File", directory)
             self._file_dialog.setAcceptMode(QFileDialog.AcceptOpen)
             mime_types = ["audio/mpeg", "audio/aac", "audio/x-ms-wma",
-                         "audio/x-flac+ogg", "audio/x-wav"]
+                          "audio/x-flac+ogg", "audio/x-wav"]
             self._file_dialog.setMimeTypeFilters(mime_types)
             self._file_dialog.selectMimeTypeFilter(mime_types[0])
 
index 1d1dba86e840ac282e08f20a87973a64a2991547..78295adf16a77af60bc52370eee63c907b19b1ab 100644 (file)
@@ -13,7 +13,8 @@ class BookDelegate(QSqlRelationalDelegate):
 
     def __init__(self, parent=None):
         QSqlRelationalDelegate.__init__(self, parent)
-        self.star = QPixmap(":/images/star.png")
+        self.star = QPixmap(":/images/star.svg")
+        self.star_filled = QPixmap(":/images/star-filled.svg")
 
     def paint(self, painter, option, index):
         """ Paint the items in the table.
@@ -43,17 +44,19 @@ class BookDelegate(QSqlRelationalDelegate):
 
             if option.state & QStyle.State_Selected:
                 painter.fillRect(option.rect,
-                    option.palette.color(color_group, QPalette.Highlight))
+                                 option.palette.color(color_group, QPalette.Highlight))
             rating = model.data(index, Qt.DisplayRole)
             width = self.star.width()
             height = self.star.height()
             x = option.rect.x()
             y = option.rect.y() + (option.rect.height() / 2) - (height / 2)
-            for i in range(rating):
-                painter.drawPixmap(x, y, self.star)
+            for i in range(5):
+                if i < rating:
+                    painter.drawPixmap(x, y, self.star_filled)
+                else:
+                    painter.drawPixmap(x, y, self.star)
                 x += width
 
-
         pen = painter.pen()
         painter.setPen(option.palette.color(QPalette.Mid))
         painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
index d6ad21337c66a6291ef2247f706248d7bb3f6c33..a52ee381b5072c162a5179ba3220f5ac68a3858e 100644 (file)
@@ -1,5 +1,6 @@
 <!DOCTYPE RCC><RCC version="1.0">
 <qresource>
-  <file>images/star.png</file>
+  <file>images/star.svg</file>
+  <file>images/star-filled.svg</file>
 </qresource>
 </RCC>
index 4acb63d44a6dc5c1e71e7db2a2d32c332b7c61a7..7f9e0f94b8e0ed9fde99ef25a797a80003018732 100644 (file)
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
 from PySide6.QtWidgets import (QAbstractItemView, QDataWidgetMapper,
-    QHeaderView, QMainWindow, QMessageBox)
+                               QHeaderView, QMainWindow, QMessageBox)
 from PySide6.QtGui import QKeySequence
 from PySide6.QtSql import QSqlRelation, QSqlRelationalTableModel, QSqlTableModel
 from PySide6.QtCore import Qt, Slot
@@ -58,7 +58,7 @@ class BookWindow(QMainWindow, Ui_BookWindow):
 
         # Lock and prohibit resizing of the width of the rating column:
         self.bookTable.horizontalHeader().setSectionResizeMode(model.fieldIndex("rating"),
-            QHeaderView.ResizeToContents)
+                                                               QHeaderView.ResizeToContents)
 
         mapper = QDataWidgetMapper(self)
         mapper.setModel(model)
@@ -77,22 +77,22 @@ class BookWindow(QMainWindow, Ui_BookWindow):
 
     def showError(self, err):
         QMessageBox.critical(self, "Unable to initialize Database",
-                    f"Error initializing database: {err.text()}")
+                             f"Error initializing database: {err.text()}")
 
     def create_menubar(self):
         file_menu = self.menuBar().addMenu(self.tr("&File"))
         quit_action = file_menu.addAction(self.tr("&Quit"))
-        quit_action.triggered.connect(qApp.quit)
+        quit_action.triggered.connect(qApp.quit)  # noqa: F821
 
         help_menu = self.menuBar().addMenu(self.tr("&Help"))
         about_action = help_menu.addAction(self.tr("&About"))
         about_action.setShortcut(QKeySequence.HelpContents)
         about_action.triggered.connect(self.about)
         aboutQt_action = help_menu.addAction("&About Qt")
-        aboutQt_action.triggered.connect(qApp.aboutQt)
+        aboutQt_action.triggered.connect(qApp.aboutQt)  # noqa: F821
 
     @Slot()
     def about(self):
         QMessageBox.about(self, self.tr("About Books"),
-            self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
-                "with a model/view framework."))
+                          self.tr("<p>The <b>Books</b> example shows how to use Qt SQL classes "
+                          "with a model/view framework."))
diff --git a/examples/sql/books/images/star-filled.svg b/examples/sql/books/images/star-filled.svg
new file mode 100644 (file)
index 0000000..8a2aee2
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#0d0d0d"><path d="M8.41 18.138L12 15.845l3.59 2.323-.94-4.345 3.162-2.897-4.159-.392L12 6.43l-1.652 4.073-4.159.392 3.162 2.927-.94 4.315zm-1.346 3.696a1.04 1.04 0 0 1-1.567-1.104l1.318-6.033-4.476-4.11c-.665-.611-.293-1.726.604-1.808l5.866-.539 2.229-5.587c.348-.872 1.575-.872 1.923 0l2.229 5.587 5.866.539c.897.082 1.269 1.197.604 1.808l-4.476 4.11 1.318 6.033a1.04 1.04 0 0 1-1.567 1.104L12 18.681l-4.935 3.153z"/><path d="M12 5l-1.796 5.528H4.392l4.702 3.416-1.796 5.528L12 16.056l4.702 3.416-1.796-5.528 4.702-3.416h-5.812L12 5z"/></svg>
diff --git a/examples/sql/books/images/star.png b/examples/sql/books/images/star.png
deleted file mode 100644 (file)
index 87f4464..0000000
Binary files a/examples/sql/books/images/star.png and /dev/null differ
diff --git a/examples/sql/books/images/star.svg b/examples/sql/books/images/star.svg
new file mode 100644 (file)
index 0000000..d959abc
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none"><path d="M8.41 18.138L12 15.845l3.59 2.323-.94-4.345 3.162-2.897-4.159-.392L12 6.43l-1.652 4.073-4.159.392 3.162 2.927-.94 4.315zm-1.346 3.696a1.04 1.04 0 0 1-1.567-1.104l1.318-6.033-4.476-4.11c-.665-.611-.293-1.726.604-1.808l5.866-.539 2.229-5.587c.348-.872 1.575-.872 1.923 0l2.229 5.587 5.866.539c.897.082 1.269 1.197.604 1.808l-4.476 4.11 1.318 6.033a1.04 1.04 0 0 1-1.567 1.104L12 18.681l-4.935 3.153z" fill="#0d0d0d"/></svg>
index 9a6575dc2453a2fea61d06f6e0a4794488668ffa..025b5588471a81482c2197eaeff6d1caaa4c0603 100644 (file)
@@ -4,7 +4,7 @@
 import sys
 from PySide6.QtWidgets import QApplication
 from bookwindow import BookWindow
-import rc_books
+import rc_books  # noqa: F401
 
 if __name__ == "__main__":
     app = QApplication([])
index b589380e4ca016e590bfd5515802ee656dd958c0..123d50de89b7ab9a827032fde2c8561fe9c9d05b 100644 (file)
@@ -1,62 +1,86 @@
 # Resource object code (Python 3)
 # Created by: object code
-# Created by: The Resource Compiler for Qt version 6.2.2
+# Created by: The Resource Compiler for Qt version 6.6.1
 # WARNING! All changes made in this file will be lost!
 
 from PySide6 import QtCore
 
 qt_resource_data = b"\
-\x00\x00\x03\x0e\
-\x89\
-PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\
-\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xffa\
-\x00\x00\x00\x09pHYs\x00\x00\x0b\x11\x00\x00\x0b\x11\
-\x01\x7fd_\x91\x00\x00\x00\x07tIME\x07\xd4\x09\
-\x03\x12\x11\x08\x18~\xe5:\x00\x00\x00\x06bKGD\
-\x00\xff\x00\xff\x00\xff\xa0\xbd\xa7\x93\x00\x00\x02\x9bID\
-AT8\xcbc\x98:c\x1e#:\xe6\xe5d\xcf\x17\
-\x12\x12\x16\xc4&\x87\x8e\x19\xb0\x09v\xc6\x18\xb7x\xea\
-\x8b\xcd\x9c=o\x09i\x06,X4\x8f\xf1\xd2\xa5\x99\
-L\xb9\xa1\x16\xc5\xc7\xbb\xed\xff\x0a\xf2\xb2;M\x9f\xb5\
-\x908\x03\x16,\x9a\xcb\xf8\xe0\xde\x04\x96\xc7\x0f\xdby\
-\xe7MO\xc8\xfbv\xbf\xe5\xff\xb4\x0a\x9b\x9by\x851\
-\xdc\xd3g-\x82k\x983\x7f)\xe3l F1`\
-\xca\xf4y\x8c\xd7\xaeMg\x02i~\xf2\xa8Y\xe1\xd2\
-\xa5\xfa\xdc_\x9f7\xfd\xffx\xbf\xea\x7fE\x96m\x97\
-\x81\x81>'33\x8b\xa5\x9e8gi\xb8\x9e\xc0f\
-&&\xa6D\x14\x03&N\x9d\xc7x\xef\xdeD\x96'\
-\x0f[E\x9f>j\xd6\xbdu\xb3\x22\xef\xd7\xb7=\xff\
-\xbe\x7f\xe8\xfb\x7f~S\xcc\xef\x05\xc5\xea\x9fNOQ\
-\xfb\x7f\xbaM\xed\xbf\x87\x1a\xefn5-\x1dV\x14\x03\
-f\xcf[\xce\xa8\xa4\xa9![W\xed\x9b}\xefJ\xcb\
-\xcew\xaf&\x7f\xfa\xfee\xc9\xff\xef\x1f\xfa\xff\xbf\xbf\
-\x95\xf2\xff\xc9^\x83\xffW\x17\xaa\xfdot\x12{\xc4\
-\xc7\xc7/\x8e\x12\x06Y\xb9\x85\xcc\xb2\x82\x1c\xf3\xa7D\
-\xab\xfe\xfa\xbe%\xe2\xff\x8fgm\xff\x7f|\x9a\x08\xd6\
-\xfc\xf5Y\xcd\xff\xd7\xe7\xfc\xfe\xdf\xde\xa0\xf5\x7fE\x94\
-\xecO\x16\x16V\xebi3\xe7\xa3\x06\xe2\xe4is\x18\
-\xe7/Z\xc1\xc8\xce\xc1i\x10\xe5\xa8\xd2\xbe\xa6\xcd\xe7\
-\xf6\xc3m\x99\xff~^(\xf8\xff\xe1j\xe0\xff\x17G\
-L\xff\xdf\xdf\xae\xf6\xbf\xc2]\xf4\xba\x9a\x9a\x06\x1bF\
-,\x00\x01#2\xe6\xe6\xe6`Q\xd6\x941_\xde\xe4\
-q\xfb\xc3y\xd3\xff\x1b\x8aT\xff\xbf?`\xff\xff\xdc\
-l\xe5\xff\xea\xc2\x1c9\xd3g-\xc0i\x00\x13\x10\x8b\
-\x03\xb1?\x10\xe7\xf5\x16\xd8\xde\xf8p\xc6\xe4\xbf\x9d<\
-\xf7t\x7fC\xe9\x95\xb7\x96\xd9\xff\x9b\x9c,\xfdN@\
-@H\x14\x9b\x01LP,\x06\xc4\x19@|\x22;\xca\
-\xf0\xe7\xe9\xf9\x06\xff\x81\xec\x03@\xbc^\x82\x9f\xf3\xf6\
-\x9e\x1a\xf3_az\x823P\xd2\x01T#3\x10\x0b\
-\x00\xb1\x1e\x10\x17\x03\xf1\xd1\xa8@\xdd\x9f\xad\x09J \
-\x03\xfe\x00\xf17 >\x0f\xb4kf\xb9\xa7\xea\x0d}\
-i>#d\x03\xb4\x808\x08\x88k\x81x\x09\xd4\xc6\
-\x1b\x11a\x06\xdf\xec\x94\xb8\xdf\x03\xd9;\x81x\x1a\x10\
-\xf7\x82\xd4\xb0\xb2\xb1G\xf9\xda\x99:L\x9d9\x9f\x09\
-f\x80\x0e\x10;\x02\xb1\x13\x10[\x00\xb1\x01\x10\x07\x06\
-{h\x9c\x02\xd2k\xa0\x86\x8b\x001\x17\x10\xf3\x80\xb0\
-\x88\x88(\xcb,hFC\xf6\x02\x08\xb3\x001+\x10\
-K122\xe4\x01i7 \x96\x01b6\xa88\x0b\
-T=cW\xef$\xb0\x01\x00\xceo{\xf5UL\xf0\
-\xac\x00\x00\x00\x00IEND\xaeB`\x82\
+\x00\x00\x02e\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 width=\x2224\
+\x22 height=\x2224\x22 fi\
+ll=\x22#0d0d0d\x22><pa\
+th d=\x22M8.41 18.1\
+38L12 15.845l3.5\
+9 2.323-.94-4.34\
+5 3.162-2.897-4.\
+159-.392L12 6.43\
+l-1.652 4.073-4.\
+159.392 3.162 2.\
+927-.94 4.315zm-\
+1.346 3.696a1.04\
+ 1.04 0 0 1-1.56\
+7-1.104l1.318-6.\
+033-4.476-4.11c-\
+.665-.611-.293-1\
+.726.604-1.808l5\
+.866-.539 2.229-\
+5.587c.348-.872 \
+1.575-.872 1.923\
+ 0l2.229 5.587 5\
+.866.539c.897.08\
+2 1.269 1.197.60\
+4 1.808l-4.476 4\
+.11 1.318 6.033a\
+1.04 1.04 0 0 1-\
+1.567 1.104L12 1\
+8.681l-4.935 3.1\
+53z\x22/><path d=\x22M\
+12 5l-1.796 5.52\
+8H4.392l4.702 3.\
+416-1.796 5.528L\
+12 16.056l4.702 \
+3.416-1.796-5.52\
+8 4.702-3.416h-5\
+.812L12 5z\x22/></s\
+vg>\x0a\
+\x00\x00\x01\xfa\
+<\
+svg xmlns=\x22http:\
+//www.w3.org/200\
+0/svg\x22 width=\x2224\
+\x22 height=\x2224\x22 fi\
+ll=\x22none\x22><path \
+d=\x22M8.41 18.138L\
+12 15.845l3.59 2\
+.323-.94-4.345 3\
+.162-2.897-4.159\
+-.392L12 6.43l-1\
+.652 4.073-4.159\
+.392 3.162 2.927\
+-.94 4.315zm-1.3\
+46 3.696a1.04 1.\
+04 0 0 1-1.567-1\
+.104l1.318-6.033\
+-4.476-4.11c-.66\
+5-.611-.293-1.72\
+6.604-1.808l5.86\
+6-.539 2.229-5.5\
+87c.348-.872 1.5\
+75-.872 1.923 0l\
+2.229 5.587 5.86\
+6.539c.897.082 1\
+.269 1.197.604 1\
+.808l-4.476 4.11\
+ 1.318 6.033a1.0\
+4 1.04 0 0 1-1.5\
+67 1.104L12 18.6\
+81l-4.935 3.153z\
+\x22 fill=\x22#0d0d0d\x22\
+/></svg>\x0a\
 "
 
 qt_resource_name = b"\
@@ -64,19 +88,25 @@ qt_resource_name = b"\
 \x07\x03}\xc3\
 \x00i\
 \x00m\x00a\x00g\x00e\x00s\
+\x00\x0f\
+\x02\x11 \x07\
+\x00s\
+\x00t\x00a\x00r\x00-\x00f\x00i\x00l\x00l\x00e\x00d\x00.\x00s\x00v\x00g\
 \x00\x08\
-\x0a\x85X\x07\
+\x0a\x85U\x87\
 \x00s\
-\x00t\x00a\x00r\x00.\x00p\x00n\x00g\
+\x00t\x00a\x00r\x00.\x00s\x00v\x00g\
 "
 
 qt_resource_struct = b"\
 \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
 \x00\x00\x00\x00\x00\x00\x00\x00\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x00\x12\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01z\xe7\xee&\xfd\
+\x00\x00\x01\x8c\xd4\xc79\xcf\
+\x00\x00\x006\x00\x00\x00\x00\x00\x01\x00\x00\x02i\
+\x00\x00\x01\x8c\xd4\xc79\xcf\
 "
 
 def qInitResources():
index a43d4d1bce26d91dd51d2d6405e4a77c20562d0a..f0ce9c28c2ced0c128c6f79c8b923ec29e039a29 100644 (file)
@@ -18,11 +18,11 @@ class MovementTransition(QEventTransition):
         self.window = window
 
     def eventTest(self, event):
-        if (event.type() == QEvent.StateMachineWrapped and
-                event.event().type() == QEvent.KeyPress):
+        if (event.type() == QEvent.StateMachineWrapped
+                and event.event().type() == QEvent.KeyPress):
             key = event.event().key()
-            return (key == Qt.Key_2 or key == Qt.Key_8 or
-                    key == Qt.Key_6 or key == Qt.Key_4)
+            return (key == Qt.Key_2 or key == Qt.Key_8
+                    or key == Qt.Key_6 or key == Qt.Key_4)
         return False
 
     def onTransition(self, event):
@@ -74,8 +74,8 @@ class MainWindow(QMainWindow):
         for x in range(self.width):
             column = []
             for y in range(self.height):
-                if (x == 0 or x == self.width - 1 or y == 0 or
-                        y == self.height - 1 or generator.bounded(0, 40) == 0):
+                if (x == 0 or x == self.width - 1 or y == 0
+                        or y == self.height - 1 or generator.bounded(0, 40) == 0):
                     column.append('#')
                 else:
                     column.append('.')
@@ -113,7 +113,7 @@ class MainWindow(QMainWindow):
         input_state.addTransition(quit_transition)
 
         machine.setInitialState(input_state)
-        machine.finished.connect(qApp.quit)
+        machine.finished.connect(qApp.quit)  # noqa: F821
         machine.start()
 
     def sizeHint(self):
index feb56ed1c07944c101db6e3ebd79780e68b2895b..e8333e2f9b3460ad7e653cc3c2410e7074debebc 100644 (file)
@@ -78,10 +78,12 @@ options.append(("--shiboken-module-shared-libraries-cmake",
 
 options.append(("--pyside-shared-libraries-qmake",
                 lambda: get_shared_libraries_qmake(Package.PYSIDE_MODULE), pyside_libs_error,
-                "Print paths of f{PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) for qmake"))
+                "Print paths of f{PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) "
+                "for qmake"))
 options.append(("--pyside-shared-libraries-cmake",
                 lambda: get_shared_libraries_cmake(Package.PYSIDE_MODULE), pyside_libs_error,
-                f"Print paths of {PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) for cmake"))
+                f"Print paths of {PYSIDE_MODULE} shared libraries (.so's, .dylib's, .dll's) "
+                "for cmake"))
 
 options_usage = ''
 for i, (flag, _, _, description) in enumerate(options):
@@ -153,11 +155,11 @@ def link_option(lib):
     # libraries when compiling the project
     baseName = os.path.basename(lib)
     link = ' -l'
-    if sys.platform in ['linux', 'linux2']: # Linux: 'libfoo.so' -> '/absolute/path/libfoo.so'
+    if sys.platform in ['linux', 'linux2']:  # Linux: 'libfoo.so' -> '/absolute/path/libfoo.so'
         link = lib
-    elif sys.platform in ['darwin']: # Darwin: 'libfoo.so' -> '-lfoo'
+    elif sys.platform in ['darwin']:  # Darwin: 'libfoo.so' -> '-lfoo'
         link += os.path.splitext(baseName[3:])[0]
-    else: # Windows: 'libfoo.dll' -> 'libfoo.dll'
+    else:  # Windows: 'libfoo.dll' -> 'libfoo.dll'
         link += os.path.splitext(baseName)[0]
     return link
 
@@ -212,7 +214,7 @@ def python_link_flags_qmake():
         # e.g.: "Program Files" to "Progra~1"
         for d in libdir.split("\\"):
             if " " in d:
-                libdir = libdir.replace(d, d.split(" ")[0][:-1]+"~1")
+                libdir = libdir.replace(d, d.split(" ")[0][:-1] + "~1")
         lib_flags = flags['lib']
         return f'-L{libdir} -l{lib_flags}'
     elif sys.platform == 'darwin':
index ae4543ac207755dba5f0b2fe2b0215ab94f72b3a..aee79c2aa59d09d1b87606899e88a80d9f61dc87 100644 (file)
@@ -14,7 +14,7 @@ from PySide6.QtQml import QQmlApplicationEngine, QmlElement, QmlSingleton
 from PySide6.QtGui import QGuiApplication
 from PySide6.QtWebEngineQuick import QtWebEngineQuick
 
-import rc_resources
+import rc_resources  # noqa: F401
 
 
 # To be used on the @QmlElement decorator
@@ -40,8 +40,8 @@ class Utils(QObject):
 
 
 if __name__ == '__main__':
-    QCoreApplication.setApplicationName("Quick Nano Browser");
-    QCoreApplication.setOrganizationName("QtProject");
+    QCoreApplication.setApplicationName("Quick Nano Browser")
+    QCoreApplication.setOrganizationName("QtProject")
 
     QtWebEngineQuick.initialize()
 
index f89aa97d7ec7dfffe0ec55bc8695f9d6405c4b6b..4d787f0f06d9812b16c131fb502e2e0f2619dda6 100644 (file)
@@ -9,7 +9,7 @@ from PySide6.QtCore import QCoreApplication
 from PySide6.QtWidgets import QApplication
 
 from mainwindow import MainWindow
-import rc_markdowneditor
+import rc_markdowneditor  # noqa: F401
 
 
 if __name__ == '__main__':
index bbea8610286b8d644bd0209e3551209576cdcf77..e68ce3d6f3613fda1b1aa3e9db8da52b45dc2e85 100644 (file)
@@ -51,8 +51,8 @@ class NotificationPopup(QWidget):
         self.notification.closed.connect(self.onClosed)
         QTimer.singleShot(10000, lambda: self.onClosed())
 
-        self.move(self.parentWidget().mapToGlobal(self.parentWidget().rect().bottomRight() -
-                  QPoint(self.width() + 10, self.height() + 10)))
+        self.move(self.parentWidget().mapToGlobal(self.parentWidget().rect().bottomRight()
+                                                  - QPoint(self.width() + 10, self.height() + 10)))
 
     @Slot()
     def onClosed(self):
index 4dc65bfa3bd44e4e44dc1155577a4c69a91364bb..a124ea084df093064246a2f66efc6df0570cd6dd 100644 (file)
@@ -33,7 +33,8 @@ class Browser(QObject):
             s.setAttribute(QWebEngineSettings.DnsPrefetchEnabled, True)
             s.setAttribute(QWebEngineSettings.LocalContentCanAccessRemoteUrls, True)
             s.setAttribute(QWebEngineSettings.LocalContentCanAccessFileUrls, False)
-            self._profile.downloadRequested.connect(self._download_manager_widget.download_requested)
+            self._profile.downloadRequested.connect(
+                self._download_manager_widget.download_requested)
 
         profile = QWebEngineProfile.defaultProfile() if offTheRecord else self._profile
         main_window = BrowserWindow(self, profile, False)
index 57686574268960b187d7aa038394bede243ead36..4efdfbdd4290f415f44c53ec92310d01f5a3b069 100644 (file)
@@ -84,7 +84,8 @@ class BrowserWindow(QMainWindow):
         if not forDevTools:
             self._tab_widget.link_hovered.connect(self._show_status_message)
             self._tab_widget.load_progress.connect(self.handle_web_view_load_progress)
-            self._tab_widget.web_action_enabled_changed.connect(self.handle_web_action_enabled_changed)
+            self._tab_widget.web_action_enabled_changed.connect(
+                self.handle_web_action_enabled_changed)
             self._tab_widget.url_changed.connect(self._url_changed)
             self._tab_widget.fav_icon_changed.connect(self._fav_action.setIcon)
             self._tab_widget.dev_tools_requested.connect(self.handle_dev_tools_requested)
@@ -328,7 +329,7 @@ class BrowserWindow(QMainWindow):
 
     def create_help_menu(self):
         help_menu = QMenu("Help")
-        help_menu.addAction("About Qt", qApp.aboutQt)
+        help_menu.addAction("About Qt", qApp.aboutQt)  # noqa: F821
         return help_menu
 
     @Slot()
index 89dc2889a368a9f78c6f8aa886816a3226884b41..6f3dbf78f52c0bc7673c4dba97be4930599e899f 100644 (file)
@@ -25,7 +25,6 @@ def with_unit(bytes):
 class DownloadWidget(QFrame):
     """Displays one ongoing or finished download (QWebEngineDownloadRequest)."""
 
-
     # This signal is emitted when the user indicates that they want to remove
     # this download from the downloads list.
     remove_clicked = Signal(QWidget)
index 8d0946cc9b0ee467629d8ee4983d1cfd1d1e6c4f..781ec29eb4654c6a2f7f5a6a6b631ffd22392dd3 100644 (file)
@@ -13,7 +13,7 @@ from PySide6.QtCore import QCoreApplication, QLoggingCategory, QUrl
 
 from browser import Browser
 
-import data.rc_simplebrowser
+import data.rc_simplebrowser  # noqa: F401
 
 if __name__ == "__main__":
     parser = ArgumentParser(description="Qt Widgets Web Browser",
index 05932eec92a80a98df2c90671f39647703c4862c..84d24833bfca81db40935ff56b9be32c8b36bb19 100644 (file)
@@ -99,8 +99,10 @@ class WebView(QWebEngineView):
             old_page.createCertificateErrorDialog.disconnect(self.handle_certificate_error)
             old_page.authenticationRequired.disconnect(self.handle_authentication_required)
             old_page.featurePermissionRequested.disconnect(self.handle_feature_permission_requested)
-            old_page.proxyAuthenticationRequired.disconnect(self.handle_proxy_authentication_required)
-            old_page.registerProtocolHandlerRequested.disconnect(self.handle_register_protocol_handler_requested)
+            old_page.proxyAuthenticationRequired.disconnect(
+                self.handle_proxy_authentication_required)
+            old_page.registerProtocolHandlerRequested.disconnect(
+                self.handle_register_protocol_handler_requested)
             old_page.fileSystemAccessRequested.disconnect(self.handle_file_system_access_requested)
 
         self.create_web_action_trigger(page, QWebEnginePage.Forward)
@@ -112,7 +114,8 @@ class WebView(QWebEngineView):
         page.authenticationRequired.connect(self.handle_authentication_required)
         page.featurePermissionRequested.connect(self.handle_feature_permission_requested)
         page.proxyAuthenticationRequired.connect(self.handle_proxy_authentication_required)
-        page.registerProtocolHandlerRequested.connect(self.handle_register_protocol_handler_requested)
+        page.registerProtocolHandlerRequested.connect(
+            self.handle_register_protocol_handler_requested)
         page.fileSystemAccessRequested.connect(self.handle_file_system_access_requested)
 
     def load_progress(self):
@@ -229,8 +232,7 @@ class WebView(QWebEngineView):
         question = question_for_feature(feature).replace("%1", host)
         w = self.window()
         page = self.page()
-        if (question
-            and QMessageBox.question(w, title, question) == QMessageBox.Yes):
+        if question and QMessageBox.question(w, title, question) == QMessageBox.Yes:
             page.setFeaturePermission(securityOrigin, feature,
                                       QWebEnginePage.PermissionGrantedByUser)
         else:
index 48f60c6a801342493395cbf6cdcb08ccc96f1cf9..2db865996095ac15cd8c5fafd9f117261f927218 100644 (file)
@@ -7,7 +7,7 @@ import sys
 from PySide6.QtCore import QUrl, Slot
 from PySide6.QtGui import QIcon
 from PySide6.QtWidgets import (QApplication, QLineEdit,
-    QMainWindow, QPushButton, QToolBar)
+                               QMainWindow, QPushButton, QToolBar)
 from PySide6.QtWebEngineCore import QWebEnginePage
 from PySide6.QtWebEngineWidgets import QWebEngineView
 
index b621ec11753e9e7ded0624e8be4bfeaba812b114..02fc75bf58f712c12a1861f77ec8edc04f1347bf 100644 (file)
@@ -15,7 +15,7 @@ from PySide6.QtWidgets import (QApplication, QGraphicsItem, QGraphicsPixmapItem,
                                QGraphicsWidget, QStyle)
 from PySide6.QtStateMachine import QState, QStateMachine
 
-import animatedtiles_rc
+import animatedtiles_rc  # noqa: F401
 
 
 # Deriving from more than one wrapped class is not supported, so we use
@@ -95,7 +95,7 @@ class Button(QGraphicsWidget):
 
         painter.drawEllipse(r.adjusted(5, 5, -5, -5))
         painter.drawPixmap(-self._pix.width() / 2, -self._pix.height() / 2,
-                self._pix)
+                           self._pix)
 
     def mousePressEvent(self, ev):
         self.pressed.emit()
@@ -123,7 +123,7 @@ if __name__ == '__main__':
     for i in range(64):
         item = Pixmap(kinetic_pix)
         item.pixmap_item.setOffset(-kinetic_pix.width() / 2,
-                -kinetic_pix.height() / 2)
+                                   -kinetic_pix.height() / 2)
         item.pixmap_item.setZValue(i)
         items.append(item)
         scene.addItem(item.pixmap_item)
@@ -161,23 +161,25 @@ if __name__ == '__main__':
     for i, item in enumerate(items):
         # Ellipse.
         ellipse_state.assignProperty(item, 'pos',
-                QPointF(math.cos((i / 63.0) * 6.28) * 250,
-                        math.sin((i / 63.0) * 6.28) * 250))
+                                     QPointF(math.cos((i / 63.0) * 6.28) * 250,
+                                             math.sin((i / 63.0) * 6.28) * 250))
 
         # Figure 8.
         figure_8state.assignProperty(item, 'pos',
-                QPointF(math.sin((i / 63.0) * 6.28) * 250,
-                        math.sin(((i * 2) / 63.0) * 6.28) * 250))
+                                     QPointF(math.sin((i / 63.0) * 6.28) * 250,
+                                             math.sin(((i * 2) / 63.0) * 6.28) * 250))
 
         # Random.
         random_state.assignProperty(item, 'pos',
-                QPointF(-250 + generator.bounded(0, 500),
-                               -250 + generator.bounded(0, 500)))
+                                    QPointF(-250 + generator.bounded(0, 500),
+                                            -250 + generator.bounded(0, 500)))
 
         # Tiled.
+        width = kinetic_pix.width()
+        height = kinetic_pix.height()
         tiled_state.assignProperty(item, 'pos',
-                QPointF(((i % 8) - 4) * kinetic_pix.width() + kinetic_pix.width() / 2,
-                        ((i // 8) - 4) * kinetic_pix.height() + kinetic_pix.height() / 2))
+                                   QPointF(((i % 8) - 4) * width + width / 2,
+                                           ((i // 8) - 4) * height + height / 2))
 
         # Centered.
         centered_state.assignProperty(item, 'pos', QPointF())
@@ -188,8 +190,7 @@ if __name__ == '__main__':
     view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
     view.setBackgroundBrush(QBrush(bg_pix))
     view.setCacheMode(QGraphicsView.CacheBackground)
-    view.setRenderHints(
-            QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
+    view.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
     view.show()
 
     states = QStateMachine()
index e49f37decb50fc5bafc62da983c393af1e195145..610050210d852300caaf0e13beff4a7201f050af 100644 (file)
@@ -11,7 +11,7 @@ from PySide6.QtWidgets import (QApplication, QGraphicsScene, QGraphicsView,
                                QGraphicsWidget)
 from PySide6.QtStateMachine import QState, QStateMachine
 
-import appchooser_rc
+import appchooser_rc  # noqa: F401
 
 
 class Pixmap(QGraphicsWidget):
index f8cba0060031d37170af04dc96794937f9f0c069..ba50324580264c6c8fcdd9c35efd603571a4f998 100644 (file)
@@ -125,7 +125,6 @@ class Window(QWidget):
 
         curve_types = [(f"QEasingCurve.{e.name}", e) for e in QEasingCurve.Type if e.value <= 40]
 
-
         with QPainter(pix) as painter:
 
             for curve_name, curve_type in curve_types:
@@ -144,14 +143,13 @@ class Window(QWidget):
 
                 # Start point.
                 painter.setBrush(Qt.red)
-                start = QPoint(y_axis,
-                        x_axis - curve_scale * curve.valueForProgress(0))
+                start = QPoint(y_axis, x_axis - curve_scale * curve.valueForProgress(0))
                 painter.drawRect(start.x() - 1, start.y() - 1, 3, 3)
 
                 # End point.
                 painter.setBrush(Qt.blue)
                 end = QPoint(y_axis + curve_scale,
-                        x_axis - curve_scale * curve.valueForProgress(1))
+                             x_axis - curve_scale * curve.valueForProgress(1))
                 painter.drawRect(end.x() - 1, end.y() - 1, 3, 3)
 
                 curve_path = QPainterPath()
@@ -159,7 +157,7 @@ class Window(QWidget):
                 t = 0.0
                 while t <= 1.0:
                     to = QPointF(y_axis + curve_scale * t,
-                            x_axis - curve_scale * curve.valueForProgress(t))
+                                 x_axis - curve_scale * curve.valueForProgress(t))
                     curve_path.lineTo(to)
                     t += 1.0 / curve_scale
 
@@ -185,14 +183,14 @@ class Window(QWidget):
         self._anim.setCurrentTime(0)
 
         is_elastic = (curve_type.value >= QEasingCurve.InElastic.value
-                    and curve_type.value <= QEasingCurve.OutInElastic.value)
+                      and curve_type.value <= QEasingCurve.OutInElastic.value)
         is_bounce = (curve_type.value >= QEasingCurve.InBounce.value
-                    and curve_type.value <= QEasingCurve.OutInBounce.value)
+                     and curve_type.value <= QEasingCurve.OutInBounce.value)
 
         self._ui.periodSpinBox.setEnabled(is_elastic)
         self._ui.amplitudeSpinBox.setEnabled(is_elastic or is_bounce)
         self._ui.overshootSpinBox.setEnabled(curve_type.value >= QEasingCurve.InBack.value
-                                          and curve_type.value <= QEasingCurve.OutInBack.value)
+                                             and curve_type.value <= QEasingCurve.OutInBack.value)
 
     def path_changed(self, index):
         self._anim.set_path_type(index)
index 9b85e837389ffcd1899e41f481b47ebd14b15f91..509c3a7aa4f3f94c26501c008138d0a4d75a1446 100644 (file)
@@ -16,7 +16,7 @@ from PySide6.QtWidgets import (QApplication, QGraphicsLinearLayout,
 
 from PySide6.QtStateMachine import QState, QStateMachine
 
-import states_rc
+import states_rc  # noqa: F401
 
 
 class Pixmap(QGraphicsObject):
index c04a33e3a234bf8641de7a6fa16abec4d4f707b1..0b5bc39dc98f638634c7fa4519aafabea6f34cb3 100644 (file)
@@ -9,7 +9,7 @@ from PySide6.QtWidgets import (QCheckBox, QComboBox, QDialog,
                                QSpinBox, QStyle, QSystemTrayIcon, QTextEdit,
                                QVBoxLayout)
 
-import rc_systray
+import rc_systray  # noqa: F401
 
 
 class Window(QDialog):
@@ -190,8 +190,8 @@ class Window(QDialog):
         self._body_label = QLabel("Body:")
 
         self._body_edit = QTextEdit()
-        self._body_edit.setPlainText("Don't believe me. Honestly, I don't have a clue."
-                                   "\nClick this balloon for details.")
+        self._body_edit.setPlainText("Don't believe me. Honestly, I don't have a clue.\n"
+                                     "Click this balloon for details.")
 
         self._show_message_button = QPushButton("Show Message")
         self._show_message_button.setDefault(True)
@@ -222,7 +222,7 @@ class Window(QDialog):
         self._restore_action.triggered.connect(self.showNormal)
 
         self._quit_action = QAction("Quit", self)
-        self._quit_action.triggered.connect(qApp.quit)
+        self._quit_action.triggered.connect(qApp.quit)  # noqa: F821
 
     def create_tray_icon(self):
         self._tray_icon_menu = QMenu(self)
index 296cff06e47c1045e4df4d966687287926be639f..0efbde69f6cd7bf52aea553d988f02dc02033dfa 100644 (file)
@@ -15,7 +15,7 @@ from PySide6.QtWidgets import (QApplication, QComboBox, QCheckBox, QFormLayout,
 
 from listchooser import PropertyChooser, SignalChooser
 
-import classwizard_rc
+import classwizard_rc  # noqa: F401
 
 
 BASE_CLASSES = ['<None>', 'PySide6.QtCore.QObject',
@@ -333,7 +333,7 @@ class OutputFilesPage(QWizardPage):
 
     def _choose_output_dir(self):
         directory = QFileDialog.getExistingDirectory(self, "Output Directory",
-                                               self.output_dir())
+                                                     self.output_dir())
         if directory:
             self.set_output_dir(directory)
 
index bf15be88c0aabc65c719277f8f36299324423ece..6bf47c6d17e69798bdad76b690142c73bbc1a3b4 100644 (file)
@@ -27,7 +27,7 @@ class ValidatingInputDialog(QDialog):
         self._lineedit = QLineEdit()
         self._lineedit.setClearButtonEnabled(True)
         re = QRegularExpression(pattern)
-        assert(re.isValid())
+        assert re.isValid()
         self._validator = QRegularExpressionValidator(re, self)
         self._lineedit.setValidator(self._validator)
         self._form_layout.addRow(label, self._lineedit)
index 8a2a4426ed3d697cc87038e9fc719b085d56a1b8..ef677d5a810197fb343ec5403439fe918dfc6967 100644 (file)
@@ -235,14 +235,14 @@ class Dialog(QDialog):
     @Slot()
     def set_integer(self):
         i, ok = QInputDialog.getInt(self,
-                "QInputDialog.getInteger()", "Percentage:", 25, 0, 100, 1)
+                                    "QInputDialog.getInteger()", "Percentage:", 25, 0, 100, 1)
         if ok:
             self._integer_label.setText(f"{i}%")
 
     @Slot()
     def set_double(self):
         d, ok = QInputDialog.getDouble(self, "QInputDialog.getDouble()",
-                "Amount:", 37.56, -10000, 10000, 2)
+                                       "Amount:", 37.56, -10000, 10000, 2)
         if ok:
             self._double_label.setText(f"${d:g}")
 
@@ -250,23 +250,21 @@ class Dialog(QDialog):
     def set_item(self):
         items = ("Spring", "Summer", "Fall", "Winter")
 
-        item, ok = QInputDialog.getItem(self, "QInputDialog.getItem()",
-                "Season:", items, 0, False)
+        item, ok = QInputDialog.getItem(self, "QInputDialog.getItem()", "Season:", items, 0, False)
         if ok and item:
             self._item_label.setText(item)
 
     @Slot()
     def set_text(self):
         text, ok = QInputDialog.getText(self, "QInputDialog.getText()",
-                "User name:", QLineEdit.Normal,
-                QDir.home().dirName())
+                                        "User name:", QLineEdit.Normal, QDir.home().dirName())
         if ok and text != '':
             self._text_label.setText(text)
 
     @Slot()
     def set_multiline_text(self):
         text, ok = QInputDialog.getMultiLineText(self, "QInputDialog::getMultiLineText()",
-                "Address:", "John Doe\nFreedom Street")
+                                                 "Address:", "John Doe\nFreedom Street")
         if ok and text != '':
             self._multiline_text_label.setText(text)
 
@@ -301,9 +299,8 @@ class Dialog(QDialog):
         options_value = self._file_options.value()
         options = QFileDialog.Options(options_value) | QFileDialog.ShowDirsOnly
 
-        directory = QFileDialog.getExistingDirectory(self,
-                "QFileDialog.getExistingDirectory()",
-                self._directory_label.text(), options)
+        directory = QFileDialog.getExistingDirectory(self, "QFileDialog.getExistingDirectory()",
+                                                     self._directory_label.text(), options)
         if directory:
             self._directory_label.setText(directory)
 
@@ -312,10 +309,9 @@ class Dialog(QDialog):
         options_value = self._file_options.value()
         options = QFileDialog.Options(options_value)
 
-        fileName, filtr = QFileDialog.getOpenFileName(self,
-                "QFileDialog.getOpenFileName()",
-                self._open_file_name_label.text(),
-                "All Files (*);;Text Files (*.txt)", "", options)
+        fileName, _ = QFileDialog.getOpenFileName(self, "QFileDialog.getOpenFileName()",
+                                                  self._open_file_name_label.text(),
+                                                  "All Files (*);;Text Files (*.txt)", "", options)
         if fileName:
             self._open_file_name_label.setText(fileName)
 
@@ -324,9 +320,9 @@ class Dialog(QDialog):
         options_value = self._file_options.value()
         options = QFileDialog.Options(options_value)
 
-        files, filtr = QFileDialog.getOpenFileNames(self,
-                "QFileDialog.getOpenFileNames()", self._open_files_path,
-                "All Files (*);;Text Files (*.txt)", "", options)
+        files, _ = QFileDialog.getOpenFileNames(self, "QFileDialog.getOpenFileNames()",
+                                                self._open_files_path,
+                                                "All Files (*);;Text Files (*.txt)", "", options)
         if files:
             self._open_files_path = files[0]
             file_list = ', '.join(files)
@@ -337,10 +333,9 @@ class Dialog(QDialog):
         options_value = self._file_options.value()
         options = QFileDialog.Options(options_value)
 
-        fileName, filtr = QFileDialog.getSaveFileName(self,
-                "QFileDialog.getSaveFileName()",
-                self._save_file_name_label.text(),
-                "All Files (*);;Text Files (*.txt)", "", options)
+        fileName, _ = QFileDialog.getSaveFileName(self, "QFileDialog.getSaveFileName()",
+                                                  self._save_file_name_label.text(),
+                                                  "All Files (*);;Text Files (*.txt)", "", options)
         if fileName:
             self._save_file_name_label.setText(fileName)
 
index 0eb9fb567a7aeee2d557756c491eb8d99a8c035f..2e551ae19bfbbaefeccc3e05a57b25106d7a4ad9 100644 (file)
@@ -15,7 +15,7 @@ def create_intro_page():
     page.setTitle("Introduction")
 
     label = QLabel("This wizard will help you register your copy of "
-            "Super Product Two.")
+                   "Super Product Two.")
     label.setWordWrap(True)
 
     layout = QVBoxLayout(page)
index 4b470cc9d1c2163f93a8f9554d70ae80e45e676d..6ffdbd70eb0a5880b0af0bc15a09e4476e09c81a 100644 (file)
@@ -2,13 +2,14 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the widgets/draganddrop/draggabletext example from Qt v5.x, originating from PyQt"""
+"""PySide6 port of the widgets/draganddrop/draggabletext example from Qt v5.x,
+   originating from PyQt"""
 
 from PySide6.QtCore import QFile, QIODevice, QMimeData, QPoint, Qt, QTextStream
 from PySide6.QtGui import QDrag, QPalette, QPixmap
 from PySide6.QtWidgets import QApplication, QFrame, QLabel, QWidget
 
-import draggabletext_rc
+import draggabletext_rc  # noqa: F401
 
 
 class DragLabel(QLabel):
index fe200357defa8d5cffe92c40ed82b78e5722f253..f074813c3c0bbb557d6b0d6b1b20e62dcf064320 100644 (file)
@@ -37,8 +37,7 @@ class Lighting(QGraphicsView):
     def setup_scene(self):
         self.m_scene.setSceneRect(-300, -200, 600, 460)
 
-        linear_grad = QLinearGradient(QPointF(-100, -100),
-                QPointF(100, 100))
+        linear_grad = QLinearGradient(QPointF(-100, -100), QPointF(100, 100))
         linear_grad.setColorAt(0, QColor(255, 255, 255))
         linear_grad.setColorAt(1, QColor(192, 192, 255))
         self.setBackgroundBrush(linear_grad)
diff --git a/examples/widgets/gettext/doc/gettext.rst b/examples/widgets/gettext/doc/gettext.rst
new file mode 100644 (file)
index 0000000..ea1127f
--- /dev/null
@@ -0,0 +1,7 @@
+.. _gettext-example:
+
+GNU gettext Example
+===================
+
+This example demonstrates the use of GNU gettext for translating
+applications as described in :ref:`translations`.
index d6e8e57b2daa0af1b55d1ebe4175ec800f4c3ce6..9d9be041f5ef487d9276b477cb13495a69a0d3b2 100644 (file)
@@ -41,7 +41,7 @@ if __name__ == '__main__':
     f = create_item(QSizeF(30, 50), QSizeF(150, 50), max_size, "F")
     g = create_item(QSizeF(30, 50), QSizeF(30, 100), max_size, "G")
 
-    l = QGraphicsAnchorLayout()
+    l = QGraphicsAnchorLayout()  # noqa: E741
     l.setSpacing(0)
 
     w = QGraphicsWidget(None, Qt.Window)
index 53c5c1aab80701c0f9ecb62ee669fb58b50b98ab..fb24db3ab9359f1bf992bfa5089cf1ffad28ed4e 100644 (file)
@@ -5,14 +5,11 @@
 import math
 import sys
 
-from PySide6.QtCore import (QLineF, QPointF, QRandomGenerator, QRectF, QTimer,
-                            Qt)
-from PySide6.QtGui import (QBrush, QColor, QPainter, QPainterPath, QPixmap,
-                           QPolygonF, QTransform)
-from PySide6.QtWidgets import (QApplication, QGraphicsItem, QGraphicsScene,
-                               QGraphicsView)
+from PySide6.QtCore import (QLineF, QPointF, QRandomGenerator, QRectF, QTimer, Qt)
+from PySide6.QtGui import (QBrush, QColor, QPainter, QPainterPath, QPixmap, QPolygonF, QTransform)
+from PySide6.QtWidgets import (QApplication, QGraphicsItem, QGraphicsScene, QGraphicsView)
 
-import mice_rc
+import mice_rc  # noqa: F401
 
 
 def random(boundary):
@@ -26,7 +23,7 @@ class Mouse(QGraphicsItem):
     # Create the bounding rectangle once.
     adjust = 0.5
     BOUNDING_RECT = QRectF(-20 - adjust, -22 - adjust, 40 + adjust,
-            83 + adjust)
+                           83 + adjust)
 
     def __init__(self):
         super().__init__()
@@ -103,7 +100,8 @@ class Mouse(QGraphicsItem):
             if angle_to_center < Mouse.PI and angle_to_center > Mouse.PI / 4:
                 # Rotate left.
                 self.angle += [-0.25, 0.25][self.angle < -Mouse.PI / 2]
-            elif angle_to_center >= Mouse.PI and angle_to_center < (Mouse.PI + Mouse.PI / 2 + Mouse.PI / 4):
+            elif (angle_to_center >= Mouse.PI
+                    and angle_to_center < (Mouse.PI + Mouse.PI / 2 + Mouse.PI / 4)):
                 # Rotate right.
                 self.angle += [-0.25, 0.25][self.angle < Mouse.PI / 2]
         elif math.sin(self.angle) < 0:
index 3348577a44c35ebb3d229ac3a5bc4ff1397c7742..60e05613ccc6bbb6c3657ec29c889c102ce1e490 100644 (file)
@@ -17,7 +17,7 @@ from PySide6.QtWidgets import (QAbstractButton, QApplication, QButtonGroup,
                                QMessageBox, QSizePolicy, QToolBox, QToolButton,
                                QWidget)
 
-import diagramscene_rc
+import diagramscene_rc  # noqa: F401
 
 
 class Arrow(QGraphicsLineItem):
@@ -30,8 +30,7 @@ class Arrow(QGraphicsLineItem):
         self._my_end_item = endItem
         self.setFlag(QGraphicsItem.ItemIsSelectable, True)
         self._my_color = Qt.black
-        self.setPen(QPen(self._my_color, 2, Qt.SolidLine,
-                Qt.RoundCap, Qt.RoundJoin))
+        self.setPen(QPen(self._my_color, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin))
 
     def set_color(self, color):
         self._my_color = color
@@ -163,19 +162,19 @@ class DiagramItem(QGraphicsPolygonItem):
             self._my_polygon = path.toFillPolygon()
         elif self.diagram_type == self.Conditional:
             self._my_polygon = QPolygonF([
-                    QPointF(-100, 0), QPointF(0, 100),
-                    QPointF(100, 0), QPointF(0, -100),
-                    QPointF(-100, 0)])
+                QPointF(-100, 0), QPointF(0, 100),
+                QPointF(100, 0), QPointF(0, -100),
+                QPointF(-100, 0)])
         elif self.diagram_type == self.Step:
             self._my_polygon = QPolygonF([
-                    QPointF(-100, -100), QPointF(100, -100),
-                    QPointF(100, 100), QPointF(-100, 100),
-                    QPointF(-100, -100)])
+                QPointF(-100, -100), QPointF(100, -100),
+                QPointF(100, 100), QPointF(-100, 100),
+                QPointF(-100, -100)])
         else:
             self._my_polygon = QPolygonF([
-                    QPointF(-120, -80), QPointF(-70, 80),
-                    QPointF(120, 80), QPointF(70, -80),
-                    QPointF(-120, -80)])
+                QPointF(-120, -80), QPointF(-70, 80),
+                QPointF(120, 80), QPointF(70, -80),
+                QPointF(-120, -80)])
 
         self.setPolygon(self._my_polygon)
         self.setFlag(QGraphicsItem.ItemIsMovable, True)
@@ -291,8 +290,7 @@ class DiagramScene(QGraphicsScene):
             item.setPos(mouseEvent.scenePos())
             self.item_inserted.emit(item)
         elif self._my_mode == self.InsertLine:
-            self.line = QGraphicsLineItem(QLineF(mouseEvent.scenePos(),
-                                        mouseEvent.scenePos()))
+            self.line = QGraphicsLineItem(QLineF(mouseEvent.scenePos(), mouseEvent.scenePos()))
             self.line.setPen(QPen(self._my_line_color, 2))
             self.addItem(self.line)
         elif self._my_mode == self.InsertText:
@@ -328,10 +326,10 @@ class DiagramScene(QGraphicsScene):
             self.removeItem(self.line)
             self.line = None
 
-            if (len(start_items) and len(end_items) and
-                    isinstance(start_items[0], DiagramItem) and
-                    isinstance(end_items[0], DiagramItem) and
-                    start_items[0] != end_items[0]):
+            if (len(start_items) and len(end_items)
+                    and isinstance(start_items[0], DiagramItem)
+                    and isinstance(end_items[0], DiagramItem)
+                    and start_items[0] != end_items[0]):
                 start_item = start_items[0]
                 end_item = end_items[0]
                 arrow = Arrow(start_item, end_item)
@@ -484,24 +482,21 @@ class MainWindow(QMainWindow):
     def text_color_changed(self):
         self._text_action = self.sender()
         self._font_color_tool_button.setIcon(self.create_color_tool_button_icon(
-                    ':/images/textpointer.png',
-                    QColor(self._text_action.data())))
+            ':/images/textpointer.png', QColor(self._text_action.data())))
         self.text_button_triggered()
 
     @Slot()
     def item_color_changed(self):
         self._fill_action = self.sender()
         self._fill_color_tool_button.setIcon(self.create_color_tool_button_icon(
-                    ':/images/floodfill.png',
-                    QColor(self._fill_action.data())))
+            ':/images/floodfill.png', QColor(self._fill_action.data())))
         self.fill_button_triggered()
 
     @Slot()
     def line_color_changed(self):
         self._line_action = self.sender()
         self._line_color_tool_button.setIcon(self.create_color_tool_button_icon(
-                    ':/images/linecolor.png',
-                    QColor(self._line_action.data())))
+            ':/images/linecolor.png', QColor(self._line_action.data())))
         self.line_button_triggered()
 
     @Slot()
@@ -541,7 +536,7 @@ class MainWindow(QMainWindow):
     @Slot()
     def about(self):
         QMessageBox.about(self, "About Diagram Scene",
-                "The <b>Diagram Scene</b> example shows use of the graphics framework.")
+                          "The <b>Diagram Scene</b> example shows use of the graphics framework.")
 
     def create_tool_box(self):
         self._button_group = QButtonGroup()
@@ -549,12 +544,9 @@ class MainWindow(QMainWindow):
         self._button_group.idClicked.connect(self.button_group_clicked)
 
         layout = QGridLayout()
-        layout.addWidget(self.create_cell_widget("Conditional", DiagramItem.Conditional),
-                0, 0)
-        layout.addWidget(self.create_cell_widget("Process", DiagramItem.Step), 0,
-                1)
-        layout.addWidget(self.create_cell_widget("Input/Output", DiagramItem.Io),
-                1, 0)
+        layout.addWidget(self.create_cell_widget("Conditional", DiagramItem.Conditional), 0, 0)
+        layout.addWidget(self.create_cell_widget("Process", DiagramItem.Step), 0, 1)
+        layout.addWidget(self.create_cell_widget("Input/Output", DiagramItem.Io), 1, 0)
 
         text_button = QToolButton()
         text_button.setCheckable(True)
@@ -580,14 +572,14 @@ class MainWindow(QMainWindow):
         self._background_button_group.buttonClicked.connect(self.background_button_group_clicked)
 
         background_layout = QGridLayout()
-        background_layout.addWidget(self.create_background_cell_widget("Blue Grid",
-                ':/images/background1.png'), 0, 0)
-        background_layout.addWidget(self.create_background_cell_widget("White Grid",
-                ':/images/background2.png'), 0, 1)
-        background_layout.addWidget(self.create_background_cell_widget("Gray Grid",
-                ':/images/background3.png'), 1, 0)
-        background_layout.addWidget(self.create_background_cell_widget("No Grid",
-                ':/images/background4.png'), 1, 1)
+        background_layout.addWidget(
+            self.create_background_cell_widget("Blue Grid", ':/images/background1.png'), 0, 0)
+        background_layout.addWidget(
+            self.create_background_cell_widget("White Grid", ':/images/background2.png'), 0, 1)
+        background_layout.addWidget(
+            self.create_background_cell_widget("Gray Grid", ':/images/background3.png'), 1, 0)
+        background_layout.addWidget(
+            self.create_background_cell_widget("No Grid", ':/images/background4.png'), 1, 1)
 
         background_layout.setRowStretch(2, 10)
         background_layout.setColumnStretch(2, 10)
@@ -603,38 +595,37 @@ class MainWindow(QMainWindow):
 
     def create_actions(self):
         self._to_front_action = QAction(
-                QIcon(':/images/bringtofront.png'), "Bring to &Front",
-                self, shortcut="Ctrl+F", statusTip="Bring item to front",
-                triggered=self.bring_to_front)
+            QIcon(':/images/bringtofront.png'), "Bring to &Front",
+            self, shortcut="Ctrl+F", statusTip="Bring item to front",
+            triggered=self.bring_to_front)
 
         self._send_back_action = QAction(
-                QIcon(':/images/sendtoback.png'), "Send to &Back", self,
-                shortcut="Ctrl+B", statusTip="Send item to back",
-                triggered=self.send_to_back)
+            QIcon(':/images/sendtoback.png'), "Send to &Back", self,
+            shortcut="Ctrl+B", statusTip="Send item to back",
+            triggered=self.send_to_back)
 
         self._delete_action = QAction(QIcon(':/images/delete.png'),
-                "&Delete", self, shortcut="Delete",
-                statusTip="Delete item from diagram",
-                triggered=self.delete_item)
+                                      "&Delete", self, shortcut="Delete",
+                                      statusTip="Delete item from diagram",
+                                      triggered=self.delete_item)
 
         self._exit_action = QAction("E&xit", self, shortcut="Ctrl+X",
-                statusTip="Quit Scenediagram example", triggered=self.close)
+                                    statusTip="Quit Scenediagram example", triggered=self.close)
 
         self._bold_action = QAction(QIcon(':/images/bold.png'),
-                "Bold", self, checkable=True, shortcut="Ctrl+B",
-                triggered=self.handle_font_change)
+                                    "Bold", self, checkable=True, shortcut="Ctrl+B",
+                                    triggered=self.handle_font_change)
 
         self._italic_action = QAction(QIcon(':/images/italic.png'),
-                "Italic", self, checkable=True, shortcut="Ctrl+I",
-                triggered=self.handle_font_change)
+                                      "Italic", self, checkable=True, shortcut="Ctrl+I",
+                                      triggered=self.handle_font_change)
 
         self._underline_action = QAction(
-                QIcon(':/images/underline.png'), "Underline", self,
-                checkable=True, shortcut="Ctrl+U",
-                triggered=self.handle_font_change)
+            QIcon(':/images/underline.png'), "Underline", self,
+            checkable=True, shortcut="Ctrl+U",
+            triggered=self.handle_font_change)
 
-        self._about_action = QAction("A&bout", self, shortcut="Ctrl+B",
-                triggered=self.about)
+        self._about_action = QAction("A&bout", self, shortcut="Ctrl+B", triggered=self.about)
 
     def create_menus(self):
         self._file_menu = self.menuBar().addMenu("&File")
@@ -669,32 +660,29 @@ class MainWindow(QMainWindow):
         self._font_color_tool_button = QToolButton()
         self._font_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
         self._font_color_tool_button.setMenu(
-                self.create_color_menu(self.text_color_changed, Qt.black))
+            self.create_color_menu(self.text_color_changed, Qt.black))
         self._text_action = self._font_color_tool_button.menu().defaultAction()
         self._font_color_tool_button.setIcon(
-                self.create_color_tool_button_icon(':/images/textpointer.png',
-                        Qt.black))
+            self.create_color_tool_button_icon(':/images/textpointer.png', Qt.black))
         self._font_color_tool_button.setAutoFillBackground(True)
         self._font_color_tool_button.clicked.connect(self.text_button_triggered)
 
         self._fill_color_tool_button = QToolButton()
         self._fill_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
         self._fill_color_tool_button.setMenu(
-                self.create_color_menu(self.item_color_changed, Qt.white))
+            self.create_color_menu(self.item_color_changed, Qt.white))
         self._fill_action = self._fill_color_tool_button.menu().defaultAction()
         self._fill_color_tool_button.setIcon(
-                self.create_color_tool_button_icon(':/images/floodfill.png',
-                        Qt.white))
+            self.create_color_tool_button_icon(':/images/floodfill.png', Qt.white))
         self._fill_color_tool_button.clicked.connect(self.fill_button_triggered)
 
         self._line_color_tool_button = QToolButton()
         self._line_color_tool_button.setPopupMode(QToolButton.MenuButtonPopup)
         self._line_color_tool_button.setMenu(
-                self.create_color_menu(self.line_color_changed, Qt.black))
+            self.create_color_menu(self.line_color_changed, Qt.black))
         self._line_action = self._line_color_tool_button.menu().defaultAction()
         self._line_color_tool_button.setIcon(
-                self.create_color_tool_button_icon(':/images/linecolor.png',
-                        Qt.black))
+            self.create_color_tool_button_icon(':/images/linecolor.png', Qt.black))
         self._line_color_tool_button.clicked.connect(self.line_button_triggered)
 
         self._text_tool_bar = self.addToolBar("Font")
@@ -719,8 +707,7 @@ class MainWindow(QMainWindow):
 
         self._pointer_type_group = QButtonGroup()
         self._pointer_type_group.addButton(pointer_button, DiagramScene.MoveItem)
-        self._pointer_type_group.addButton(line_pointer_button,
-                DiagramScene.InsertLine)
+        self._pointer_type_group.addButton(line_pointer_button, DiagramScene.InsertLine)
         self._pointer_type_group.idClicked.connect(self.pointer_group_clicked)
 
         self._scene_scale_combo = QComboBox()
@@ -775,8 +762,7 @@ class MainWindow(QMainWindow):
 
         color_menu = QMenu(self)
         for color, name in zip(colors, names):
-            action = QAction(self.create_color_icon(color), name, self,
-                    triggered=slot)
+            action = QAction(self.create_color_icon(color), name, self, triggered=slot)
             action.setData(QColor(color))
             color_menu.addAction(action)
             if color == defaultColor:
index 0bfd3e0db759c1b41d984599de852ced731ce738..9ed92b26e6fc08cd61b682ba697ca2e80e2fcdc6 100644 (file)
@@ -13,7 +13,7 @@ from PySide6.QtWidgets import (QApplication, QGraphicsItem,
                                QGraphicsItemAnimation, QGraphicsScene,
                                QGraphicsView)
 
-import dragdroprobot_rc
+import dragdroprobot_rc  # noqa: F401
 
 
 def random(boundary):
@@ -103,8 +103,8 @@ class RobotPart(QGraphicsItem):
         self.setAcceptDrops(True)
 
     def dragEnterEvent(self, event):
-        if (event.mimeData().hasColor() or
-                (isinstance(self, RobotHead) and event.mimeData().hasImage())):
+        if (event.mimeData().hasColor()
+                or (isinstance(self, RobotHead) and event.mimeData().hasImage())):
             event.setAccepted(True)
             self._drag_over = True
             self.update()
@@ -131,8 +131,7 @@ class RobotHead(RobotPart):
 
     def paint(self, painter, option, widget=None):
         if not self.pixmap:
-            painter.setBrush(self._drag_over and self.color.lighter(130)
-                                            or self.color)
+            painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
             painter.drawRoundedRect(-10, -30, 20, 30, 25, 25, Qt.RelativeSize)
             painter.setBrush(Qt.white)
             painter.drawEllipse(-7, -3 - 20, 7, 7)
@@ -154,7 +153,7 @@ class RobotTorso(RobotPart):
 
     def paint(self, painter, option, widget=None):
         painter.setBrush(self._drag_over and self.color.lighter(130)
-                                        or self.color)
+                         or self.color)
         painter.drawRoundedRect(-20, -20, 40, 60, 25, 25, Qt.RelativeSize)
         painter.drawEllipse(-25, -20, 20, 20)
         painter.drawEllipse(5, -20, 20, 20)
@@ -169,7 +168,7 @@ class RobotLimb(RobotPart):
     def paint(self, painter, option, widget=None):
         painter.setBrush(self._drag_over and self.color.lighter(130) or self.color)
         painter.drawRoundedRect(self.boundingRect(), 50, 50,
-                Qt.RelativeSize)
+                                Qt.RelativeSize)
         painter.drawEllipse(-5, -5, 10, 10)
 
 
@@ -177,35 +176,35 @@ class Robot(RobotPart):
     def __init__(self):
         super().__init__()
 
-        self.torsoItem         = RobotTorso(self)
-        self.headItem          = RobotHead(self.torsoItem)
-        self.upperLeftArmItem  = RobotLimb(self.torsoItem)
-        self.lowerLeftArmItem  = RobotLimb(self.upperLeftArmItem)
+        self.torsoItem = RobotTorso(self)
+        self.headItem = RobotHead(self.torsoItem)
+        self.upperLeftArmItem = RobotLimb(self.torsoItem)
+        self.lowerLeftArmItem = RobotLimb(self.upperLeftArmItem)
         self._upper_right_arm_item = RobotLimb(self.torsoItem)
         self._lower_right_arm_item = RobotLimb(self._upper_right_arm_item)
         self._upper_right_leg_item = RobotLimb(self.torsoItem)
         self._lower_right_leg_item = RobotLimb(self._upper_right_leg_item)
-        self.upperLeftLegItem  = RobotLimb(self.torsoItem)
-        self.lowerLeftLegItem  = RobotLimb(self.upperLeftLegItem)
+        self.upperLeftLegItem = RobotLimb(self.torsoItem)
+        self.lowerLeftLegItem = RobotLimb(self.upperLeftLegItem)
 
         self.timeline = QTimeLine()
         settings = [
-        #             item               position    rotation at
-                                        x    y    time 0  /  1
-            ( self.headItem,              0,  -18,      20,   -20 ),
-            ( self.upperLeftArmItem,    -15,  -10,     190,   180 ),
-            ( self.lowerLeftArmItem,     30,    0,      50,    10 ),
-            ( self._upper_right_arm_item,    15,  -10,     300,   310 ),
-            ( self._lower_right_arm_item,    30,    0,       0,   -70 ),
-            ( self._upper_right_leg_item,    10,   32,      40,   120 ),
-            ( self._lower_right_leg_item,    30,    0,      10,    50 ),
-            ( self.upperLeftLegItem,    -10,   32,     150,    80 ),
-            ( self.lowerLeftLegItem,     30,    0,      70,    10 ),
-            ( self.torsoItem,             0,    0,       5,   -20 )
+            #         item                  position    rotation at
+            #                                x    y    time 0  /  1
+            (self.headItem,                  0,  -18,      20,   -20),  # noqa: E241
+            (self.upperLeftArmItem,        -15,  -10,     190,   180),  # noqa: E241
+            (self.lowerLeftArmItem,         30,    0,      50,    10),  # noqa: E241
+            (self._upper_right_arm_item,    15,  -10,     300,   310),  # noqa: E241
+            (self._lower_right_arm_item,    30,    0,       0,   -70),  # noqa: E241
+            (self._upper_right_leg_item,    10,   32,      40,   120),  # noqa: E241
+            (self._lower_right_leg_item,    30,    0,      10,    50),  # noqa: E241
+            (self.upperLeftLegItem,        -10,   32,     150,    80),  # noqa: E241
+            (self.lowerLeftLegItem,         30,    0,      70,    10),  # noqa: E241
+            (self.torsoItem,                 0,    0,       5,   -20)  # noqa: E241
         ]
         self.animations = []
         for item, pos_x, pos_y, rotation1, rotation2 in settings:
-            item.setPos(pos_x,pos_y)
+            item.setPos(pos_x, pos_y)
             animation = QGraphicsItemAnimation()
             animation.setItem(item)
             animation.setTimeLine(self.timeline)
@@ -228,7 +227,7 @@ class Robot(RobotPart):
         pass
 
 
-if __name__== '__main__':
+if __name__ == '__main__':
     app = QApplication(sys.argv)
 
     scene = QGraphicsScene(-200, -200, 400, 400)
index 58d9d4a90c7c21a67bb0f6996013f8e3e72398b9..90cb496269b426b2bfff1b54bca363fd91e73def 100644 (file)
@@ -20,8 +20,6 @@ def random(boundary):
 
 class Edge(QGraphicsItem):
 
-    item_type = QGraphicsItem.UserType + 2
-
     def __init__(self, sourceNode, destNode):
         super().__init__()
 
@@ -36,7 +34,7 @@ class Edge(QGraphicsItem):
         self.adjust()
 
     def item_type(self):
-        return Edge.item_type
+        return QGraphicsItem.UserType + 2
 
     def source_node(self):
         return self.source()
@@ -119,7 +117,6 @@ class Edge(QGraphicsItem):
 
 
 class Node(QGraphicsItem):
-    item_type = QGraphicsItem.UserType + 1
 
     def __init__(self, graphWidget):
         super().__init__()
@@ -133,7 +130,7 @@ class Node(QGraphicsItem):
         self.setZValue(-1)
 
     def item_type(self):
-        return Node.item_type
+        return QGraphicsItem.UserType + 1
 
     def add_edge(self, edge):
         self._edge_list.append(weakref.ref(edge))
@@ -157,7 +154,7 @@ class Node(QGraphicsItem):
             line = QLineF(self.mapFromItem(item, 0, 0), QPointF(0, 0))
             dx = line.dx()
             dy = line.dy()
-            l = 2.0 * (dx * dx + dy * dy)
+            l = 2.0 * (dx * dx + dy * dy)  # noqa: E741
             if l > 0:
                 xvel += (dx * 150.0) / l
                 yvel += (dy * 150.0) / l
@@ -192,7 +189,7 @@ class Node(QGraphicsItem):
     def boundingRect(self):
         adjust = 2.0
         return QRectF(-10 - adjust, -10 - adjust,
-                             23 + adjust, 23 + adjust)
+                      23 + adjust, 23 + adjust)
 
     def shape(self):
         path = QPainterPath()
@@ -348,9 +345,9 @@ class GraphWidget(QGraphicsView):
         bottom_shadow = QRectF(scene_rect.left() + 5, scene_rect.bottom(),
                                scene_rect.width(), 5)
         if right_shadow.intersects(rect) or right_shadow.contains(rect):
-                painter.fillRect(right_shadow, Qt.darkGray)
+            painter.fillRect(right_shadow, Qt.darkGray)
         if bottom_shadow.intersects(rect) or bottom_shadow.contains(rect):
-                painter.fillRect(bottom_shadow, Qt.darkGray)
+            painter.fillRect(bottom_shadow, Qt.darkGray)
 
         # Fill.
         gradient = QLinearGradient(scene_rect.topLeft(), scene_rect.bottomRight())
@@ -362,7 +359,7 @@ class GraphWidget(QGraphicsView):
 
         # Text.
         text_rect = QRectF(scene_rect.left() + 4, scene_rect.top() + 4,
-                                 scene_rect.width() - 4, scene_rect.height() - 4)
+                           scene_rect.width() - 4, scene_rect.height() - 4)
         message = self.tr("Click and drag the nodes around, and zoom with the "
                           "mouse wheel or the '+' and '-' keys")
 
@@ -376,7 +373,8 @@ class GraphWidget(QGraphicsView):
         painter.drawText(text_rect, message)
 
     def scale_view(self, scaleFactor):
-        factor = self.transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width()
+        factor = self.transform().scale(scaleFactor, scaleFactor).mapRect(
+            QRectF(0, 0, 1, 1)).width()
 
         if factor < 0.07 or factor > 100:
             return
index ded7f246a3fd4e4b5a63d61ea2e256849ea1ea3c..93b1e87ba16724650fbed2b5ec4af156a5dd63a5 100644 (file)
@@ -34,7 +34,7 @@ class ImageViewer(QMainWindow):
         self._image_label = QLabel()
         self._image_label.setBackgroundRole(QPalette.Base)
         self._image_label.setSizePolicy(QSizePolicy.Ignored,
-                                       QSizePolicy.Ignored)
+                                        QSizePolicy.Ignored)
         self._image_label.setScaledContents(True)
 
         self._scroll_area = QScrollArea()
index 276a8d2b400ba6fc157bd3674e9784a3b13b72ca..ecb853e804477a3bd472947614a91c8fb134a52d 100644 (file)
@@ -15,8 +15,8 @@ class AddDialogWidget(QDialog):
 
         name_label = QLabel("Name")
         address_label = QLabel("Address")
-        button_box = QDialogButtonBox(QDialogButtonBox.Ok |
-                                      QDialogButtonBox.Cancel)
+        button_box = QDialogButtonBox(QDialogButtonBox.Ok
+                                      QDialogButtonBox.Cancel)
 
         self._name_text = QLineEdit()
         self._address_text = QTextEdit()
index 2e1f6b9b046efedc9925756ec4bf7cd3358308d4..af0cf3dee38103c5641ed3dec8ea1d7a69666b86 100644 (file)
@@ -25,16 +25,19 @@ class MainWindow(QMainWindow):
         tool_menu = self.menuBar().addMenu("&Tools")
 
         # Populate the File menu
-        open_action = self.create_action("&Open...", file_menu, self.open_file)
-        save_action = self.create_action("&Save As...", file_menu, self.save_file)
+        self.open_action = self.create_action("&Open...", file_menu, self.open_file)
+        self.save_action = self.create_action("&Save As...", file_menu, self.save_file)
         file_menu.addSeparator()
-        exit_action = self.create_action("E&xit", file_menu, self.close)
+        self.exit_action = self.create_action("E&xit", file_menu, self.close)
 
         # Populate the Tools menu
-        add_action = self.create_action("&Add Entry...", tool_menu, self._address_widget.add_entry)
-        self._edit_action = self.create_action("&Edit Entry...", tool_menu, self._address_widget.edit_entry)
+        self.add_action = self.create_action(
+            "&Add Entry...", tool_menu, self._address_widget.add_entry)
+        self._edit_action = self.create_action(
+            "&Edit Entry...", tool_menu, self._address_widget.edit_entry)
         tool_menu.addSeparator()
-        self._remove_action = self.create_action("&Remove Entry", tool_menu, self._address_widget.remove_entry)
+        self._remove_action = self.create_action(
+            "&Remove Entry", tool_menu, self._address_widget.remove_entry)
 
         # Disable the edit and remove menu items initially, as there are
         # no items yet.
index ab1330e48784ce687d78cea73e1d6e0a34960d98..cb2f46ea1bf7394e488508be05939e54d4717fd9 100644 (file)
@@ -166,8 +166,9 @@ class AddressWidget(QTabWidget):
             proxy_model.setFilterKeyColumn(0)  # Filter on the "name" column
             proxy_model.sort(0, Qt.AscendingOrder)
 
-            # This prevents an application crash (see: https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash)
-            viewselectionmodel = table_view.selectionModel()
+            # This prevents an application crash (see:
+            # https://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash)  # noqa: E501
+            self.viewselectionmodel = table_view.selectionModel()
             table_view.selectionModel().selectionChanged.connect(self.selection_changed)
 
             self.addTab(table_view, group)
index a0d63bbe28730823d0f04fc016af39784917d3c6..3c1dbd4cc3ba5c1b9bf71b5c20996cb621aa74c0 100644 (file)
@@ -105,5 +105,5 @@ class TableModel(QAbstractTableModel):
         """
         if not index.isValid():
             return Qt.ItemIsEnabled
-        return Qt.ItemFlags(QAbstractTableModel.flags(self, index) |
-                            Qt.ItemIsEditable)
+        return Qt.ItemFlags(QAbstractTableModel.flags(self, index)
+                            Qt.ItemIsEditable)
index 83423740473548cf2e10e72f059cd09c8e843834..a30b0abdfc7b7baf0a4fe4c01eeeb9b072260891 100644 (file)
@@ -46,11 +46,11 @@ class Window(QWidget):
 
         self._filter_syntax_combo_box = QComboBox()
         self._filter_syntax_combo_box.addItem("Regular expression",
-                                          REGULAR_EXPRESSION)
+                                              REGULAR_EXPRESSION)
         self._filter_syntax_combo_box.addItem("Wildcard",
-                                          WILDCARD)
+                                              WILDCARD)
         self._filter_syntax_combo_box.addItem("Fixed string",
-                                          FIXED_STRING)
+                                              FIXED_STRING)
         self._filter_syntax_label = QLabel("Filter &syntax:")
         self._filter_syntax_label.setBuddy(self._filter_syntax_combo_box)
 
@@ -147,25 +147,25 @@ def create_mail_model(parent):
     model.setHeaderData(2, Qt.Horizontal, "Date")
 
     add_mail(model, "Happy New Year!", "Grace K. <grace@software-inc.com>",
-            QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
+             QDateTime(QDate(2006, 12, 31), QTime(17, 3)))
     add_mail(model, "Radically new concept", "Grace K. <grace@software-inc.com>",
-            QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
+             QDateTime(QDate(2006, 12, 22), QTime(9, 44)))
     add_mail(model, "Accounts", "pascale@nospam.com",
-            QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
+             QDateTime(QDate(2006, 12, 31), QTime(12, 50)))
     add_mail(model, "Expenses", "Joe Bloggs <joe@bloggs.com>",
-            QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
+             QDateTime(QDate(2006, 12, 25), QTime(11, 39)))
     add_mail(model, "Re: Expenses", "Andy <andy@nospam.com>",
-            QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
+             QDateTime(QDate(2007, 1, 2), QTime(16, 5)))
     add_mail(model, "Re: Accounts", "Joe Bloggs <joe@bloggs.com>",
-            QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
+             QDateTime(QDate(2007, 1, 3), QTime(14, 18)))
     add_mail(model, "Re: Accounts", "Andy <andy@nospam.com>",
-            QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
+             QDateTime(QDate(2007, 1, 3), QTime(14, 26)))
     add_mail(model, "Sports", "Linda Smith <linda.smith@nospam.com>",
-            QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
+             QDateTime(QDate(2007, 1, 5), QTime(11, 33)))
     add_mail(model, "AW: Sports", "Rolf Newschweinstein <rolfn@nospam.com>",
-            QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
+             QDateTime(QDate(2007, 1, 5), QTime(12, 0)))
     add_mail(model, "RE: Sports", "Petra Schmidt <petras@nospam.com>",
-            QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
+             QDateTime(QDate(2007, 1, 5), QTime(12, 1)))
 
     return model
 
index aa1e6218558e77f0bcd797b988ae896a04d2b837..d1be6958e7f61314da817e0f5d2abc0957a2212d 100644 (file)
@@ -57,4 +57,3 @@ if __name__ == "__main__":
     tree.show()
 
     sys.exit(app.exec())
-
index c6abfa56862737a881f006234db2a802d6e003c2..1489bf28b5492554e6bea17e90d03b1a4da69d41 100644 (file)
@@ -52,7 +52,7 @@ class MainWindow(QMainWindow):
         self.insert_child_action.setShortcut("Ctrl+N")
         self.insert_child_action.triggered.connect(self.insert_child)
         help_menu = menubar.addMenu("&Help")
-        about_qt_action = help_menu.addAction("About Qt", qApp.aboutQt)
+        about_qt_action = help_menu.addAction("About Qt", qApp.aboutQt)  # noqa: F821
         about_qt_action.setShortcut("F1")
 
         self.setWindowTitle("Editable Tree Model")
index ecee86e38cf2eb12a16315aab615060a1163159e..5150250e0a9ae288cd9b25837595e98f7066f825 100644 (file)
@@ -49,7 +49,7 @@ class FileListModel(QAbstractListModel):
 
         if role == Qt.BackgroundRole:
             batch = row // BATCH_SIZE
-            palette = qApp.palette()
+            palette = qApp.palette()  # noqa: F821
             return palette.base() if batch % 2 == 0 else palette.alternateBase()
 
         if role == Qt.DecorationRole:
@@ -112,7 +112,7 @@ class Window(QWidget):
 
         self.setWindowTitle("Fetch More Example")
 
-    @Slot(str,int,int,int)
+    @Slot(str, int, int, int)
     def update_log(self, path, start, number, total):
         native_path = QDir.toNativeSeparators(path)
         last = start + number - 1
index 266b8c1e15dd684b60de74d2967dba98096cd757..577f0faa503610cc2a8a99e78b3fd049d79bd263 100644 (file)
@@ -10,6 +10,7 @@ from PySide6.QtCore import QModelIndex
 
 """PySide6 port of the widgets/itemviews/spinboxdelegate from Qt v6.x"""
 
+
 #! [0]
 class SpinBoxDelegate(QStyledItemDelegate):
     """A delegate that allows the user to change integer values from the model
@@ -52,7 +53,7 @@ class SpinBoxDelegate(QStyledItemDelegate):
 if __name__ == '__main__':
     app = QApplication(sys.argv)
 
-    model= QStandardItemModel(4, 2)
+    model = QStandardItemModel(4, 2)
     tableView = QTableView()
     tableView.setModel(model)
 
index 87bf1ff482a9a86230fef22632eb68c56e5cc766..973eb14f63d84cfba73b0dfd60c1e270efd1d0b1 100644 (file)
@@ -111,15 +111,15 @@ if __name__ == "__main__":
     # Create and populate the tableWidget
     table_widget = QTableWidget(4, 4)
     table_widget.setItemDelegate(StarDelegate())
-    table_widget.setEditTriggers(QAbstractItemView.DoubleClicked |
-                                QAbstractItemView.SelectedClicked)
+    table_widget.setEditTriggers(QAbstractItemView.DoubleClicked
+                                 | QAbstractItemView.SelectedClicked)
     table_widget.setSelectionBehavior(QAbstractItemView.SelectRows)
     table_widget.setHorizontalHeaderLabels(["Title", "Genre", "Artist", "Rating"])
 
-    data = [ ["Mass in B-Minor", "Baroque", "J.S. Bach", 5],
-             ["Three More Foxes", "Jazz", "Maynard Ferguson", 4],
-             ["Sex Bomb", "Pop", "Tom Jones", 3],
-             ["Barbie Girl", "Pop", "Aqua", 5] ]
+    data = [["Mass in B-Minor", "Baroque", "J.S. Bach", 5],
+            ["Three More Foxes", "Jazz", "Maynard Ferguson", 4],
+            ["Sex Bomb", "Pop", "Tom Jones", 3],
+            ["Barbie Girl", "Pop", "Aqua", 5]]
 
     for r in range(len(data)):
         table_widget.setItem(r, 0, QTableWidgetItem(data[r][0]))
index 1b44164a88fc245b3568caedf0b851d0c88e9f65..296afa9500c7063c425754ce3c75fe06a65b92e2 100644 (file)
@@ -55,8 +55,7 @@ class StarEditor(QWidget):
         """ Calculate which star the user's mouse cursor is currently
             hovering over.
         """
-        star = (x / (self.star_rating.sizeHint().width() /
-                     self.star_rating.MAX_STAR_COUNT)) + 1
+        star = (x / (self.star_rating.sizeHint().width() / self.star_rating.MAX_STAR_COUNT)) + 1
         if (star <= 0) or (star > self.star_rating.MAX_STAR_COUNT):
             return -1
 
index 4d49f7405197246a5eacdc25ff3756f7afa528cb..28dbacd6f44e09483dc46d1b076efba9146de087 100644 (file)
@@ -23,13 +23,13 @@ class StarRating(object):
         self._star_polygon.append(QPointF(1.0, 0.5))
         for i in range(1, 5):
             self._star_polygon.append(QPointF(0.5 + 0.5 * cos(0.8 * i * pi),
-                                    0.5 + 0.5 * sin(0.8 * i * pi)))
+                                      0.5 + 0.5 * sin(0.8 * i * pi)))
 
         # Create the diamond shape we'll show in the editor
         self._diamond_polygon = QPolygonF()
         diamond_points = [QPointF(0.4, 0.5), QPointF(0.5, 0.4),
-                         QPointF(0.6, 0.5), QPointF(0.5, 0.6),
-                         QPointF(0.4, 0.5)]
+                          QPointF(0.6, 0.5), QPointF(0.5, 0.6),
+                          QPointF(0.4, 0.5)]
         self._diamond_polygon.append(diamond_points)
 
     def sizeHint(self):
index a2d29e71f9ac91f21b020d055ffd67611c2f1d24..827cb78501e43276933b8a15aa33ed0b9cf5d724 100644 (file)
@@ -27,7 +27,7 @@ class Dialog(QDialog):
 
         big_editor = QTextEdit()
         big_editor.setPlainText("This widget takes up all the remaining space "
-                "in the top-level layout.")
+                                "in the top-level layout.")
 
         button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
 
@@ -75,8 +75,7 @@ class Dialog(QDialog):
             layout.addWidget(line_edit, i + 1, 1)
 
         self._small_editor = QTextEdit()
-        self._small_editor.setPlainText("This widget takes up about two thirds "
-                "of the grid layout.")
+        self._small_editor.setPlainText("This widget takes up about two thirds of the grid layout.")
 
         layout.addWidget(self._small_editor, 0, 2, 4, 1)
 
index 4e3791519784d8b131d7a005e5e96f05f8375951..c9dfcc73093220da6733ea89cb0bc5cf45080fa3 100644 (file)
@@ -74,8 +74,8 @@ class Dialog(QDialog):
 
     def show_help(self):
         QMessageBox.information(self, "Dynamic Layouts Help",
-                            "This example shows how to change layouts "
-                            "dynamically.")
+                                "This example shows how to change layouts "
+                                "dynamically.")
 
     def create_rotable_group_box(self):
         self._rotable_group_box = QGroupBox("Rotable Widgets")
@@ -102,7 +102,8 @@ class Dialog(QDialog):
         buttons_orientation_combo_box = QComboBox()
         buttons_orientation_combo_box.addItem("Horizontal", Qt.Horizontal)
         buttons_orientation_combo_box.addItem("Vertical", Qt.Vertical)
-        buttons_orientation_combo_box.currentIndexChanged[int].connect(self.buttons_orientation_changed)
+        buttons_orientation_combo_box.currentIndexChanged[int].connect(
+            self.buttons_orientation_changed)
 
         self._buttons_orientation_combo_box = buttons_orientation_combo_box
 
@@ -117,7 +118,8 @@ class Dialog(QDialog):
 
         close_button = self._button_box.addButton(QDialogButtonBox.Close)
         help_button = self._button_box.addButton(QDialogButtonBox.Help)
-        rotate_widgets_button = self._button_box.addButton("Rotate &Widgets", QDialogButtonBox.ActionRole)
+        rotate_widgets_button = self._button_box.addButton(
+            "Rotate &Widgets", QDialogButtonBox.ActionRole)
 
         rotate_widgets_button.clicked.connect(self.rotate_widgets)
         close_button.clicked.connect(self.close)
diff --git a/examples/widgets/linguist/doc/linguist.rst b/examples/widgets/linguist/doc/linguist.rst
new file mode 100644 (file)
index 0000000..24a49a6
--- /dev/null
@@ -0,0 +1,7 @@
+.. _qt-linguist-example:
+
+Qt Linguist Example
+===================
+
+This example demonstrates the use of Qt Linguist and related tools for translating
+applications as described in :ref:`translations`.
index 16655432c5b5bb8b77517fe220bf6d5317364313..e4212e801f60df21c7ec5adc985f7c563f7bdbf3 100644 (file)
@@ -9,7 +9,7 @@ from PySide6.QtWidgets import (QAbstractItemView, QApplication, QListWidget,
                                QMainWindow)
 
 
-import linguist_rc
+import linguist_rc  # noqa: F401
 
 
 class Window(QMainWindow):
@@ -21,7 +21,7 @@ class Window(QMainWindow):
         quit_action.triggered.connect(self.close)
         help_menu = self.menuBar().addMenu(self.tr("&Help"))
         about_qt_action = help_menu.addAction(self.tr("About Qt"))
-        about_qt_action.triggered.connect(qApp.aboutQt)
+        about_qt_action.triggered.connect(qApp.aboutQt)  # noqa: F821
 
         self._list_widget = QListWidget()
         self._list_widget.setSelectionMode(QAbstractItemView.MultiSelection)
index 320c421a653f3ebd9b33192649bfb0fec0b06084..7b2fa067254679b051e1c7e22278d0043ebbb62a 100644 (file)
@@ -11,7 +11,7 @@ from PySide6.QtGui import QAction, QIcon, QKeySequence
 from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow,
                                QMessageBox, QTextEdit)
 
-import application_rc
+import application_rc  # noqa: F401
 
 
 class MainWindow(QMainWindow):
@@ -73,9 +73,9 @@ class MainWindow(QMainWindow):
     @Slot()
     def about(self):
         QMessageBox.about(self, "About Application",
-                "The <b>Application</b> example demonstrates how to write "
-                "modern GUI applications using Qt, with a menu bar, "
-                "toolbars, and a status bar.")
+                          "The <b>Application</b> example demonstrates how to write "
+                          "modern GUI applications using Qt, with a menu bar, "
+                          "toolbars, and a status bar.")
 
     @Slot()
     def document_was_modified(self):
@@ -84,50 +84,51 @@ class MainWindow(QMainWindow):
     def create_actions(self):
         icon = QIcon.fromTheme("document-new", QIcon(':/images/new.png'))
         self._new_act = QAction(icon, "&New", self, shortcut=QKeySequence.New,
-                statusTip="Create a new file", triggered=self.new_file)
+                                statusTip="Create a new file", triggered=self.new_file)
 
         icon = QIcon.fromTheme("document-open", QIcon(':/images/open.png'))
         self._open_act = QAction(icon, "&Open...", self,
-                shortcut=QKeySequence.Open, statusTip="Open an existing file",
-                triggered=self.open)
+                                 shortcut=QKeySequence.Open, statusTip="Open an existing file",
+                                 triggered=self.open)
 
         icon = QIcon.fromTheme("document-save", QIcon(':/images/save.png'))
         self._save_act = QAction(icon, "&Save", self,
-                shortcut=QKeySequence.Save,
-                statusTip="Save the document to disk", triggered=self.save)
+                                 shortcut=QKeySequence.Save,
+                                 statusTip="Save the document to disk", triggered=self.save)
 
         self._save_as_act = QAction("Save &As...", self,
-                shortcut=QKeySequence.SaveAs,
-                statusTip="Save the document under a new name",
-                triggered=self.save_as)
+                                    shortcut=QKeySequence.SaveAs,
+                                    statusTip="Save the document under a new name",
+                                    triggered=self.save_as)
 
         self._exit_act = QAction("E&xit", self, shortcut="Ctrl+Q",
-                statusTip="Exit the application", triggered=self.close)
+                                 statusTip="Exit the application", triggered=self.close)
 
         icon = QIcon.fromTheme("edit-cut", QIcon(':/images/cut.png'))
         self._cut_act = QAction(icon, "Cu&t", self, shortcut=QKeySequence.Cut,
-                statusTip="Cut the current selection's contents to the clipboard",
-                triggered=self._text_edit.cut)
+                                statusTip="Cut the current selection's contents to the clipboard",
+                                triggered=self._text_edit.cut)
 
         icon = QIcon.fromTheme("edit-copy", QIcon(':/images/copy.png'))
         self._copy_act = QAction(icon, "&Copy",
-                self, shortcut=QKeySequence.Copy,
-                statusTip="Copy the current selection's contents to the clipboard",
-                triggered=self._text_edit.copy)
+                                 self, shortcut=QKeySequence.Copy,
+                                 statusTip="Copy the current selection's contents to the clipboard",
+                                 triggered=self._text_edit.copy)
 
         icon = QIcon.fromTheme("edit-paste", QIcon(':/images/paste.png'))
         self._paste_act = QAction(icon, "&Paste",
-                self, shortcut=QKeySequence.Paste,
-                statusTip="Paste the clipboard's contents into the current selection",
-                triggered=self._text_edit.paste)
+                                  self, shortcut=QKeySequence.Paste,
+                                  statusTip="Paste the clipboard's contents into the current "
+                                  "selection",
+                                  triggered=self._text_edit.paste)
 
         self._about_act = QAction("&About", self,
-                statusTip="Show the application's About box",
-                triggered=self.about)
+                                  statusTip="Show the application's About box",
+                                  triggered=self.about)
 
         self._about_qt_act = QAction("About &Qt", self,
-                statusTip="Show the Qt library's About box",
-                triggered=qApp.aboutQt)
+                                     statusTip="Show the Qt library's About box",
+                                     triggered=qApp.aboutQt)  # noqa: F821
 
         self._cut_act.setEnabled(False)
         self._copy_act.setEnabled(False)
@@ -181,10 +182,9 @@ class MainWindow(QMainWindow):
     def maybe_save(self):
         if self._text_edit.document().isModified():
             ret = QMessageBox.warning(self, "Application",
-                    "The document has been modified.\nDo you want to save "
-                    "your changes?",
-                    QMessageBox.Save | QMessageBox.Discard |
-                    QMessageBox.Cancel)
+                                      "The document has been modified.\nDo you want to save "
+                                      "your changes?",
+                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
             if ret == QMessageBox.Save:
                 return self.save()
             elif ret == QMessageBox.Cancel:
@@ -195,8 +195,7 @@ class MainWindow(QMainWindow):
         file = QFile(fileName)
         if not file.open(QFile.ReadOnly | QFile.Text):
             reason = file.errorString()
-            QMessageBox.warning(self, "Application",
-                    f"Cannot read file {fileName}:\n{reason}.")
+            QMessageBox.warning(self, "Application", f"Cannot read file {fileName}:\n{reason}.")
             return
 
         inf = QTextStream(file)
index 6d0e95a81df506af1e1d2bc91e44e7e9f150bdb7..83487ee58b1727a2fddd38130ee10ab99b9fe993 100644 (file)
@@ -2,18 +2,20 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the widgets/mainwindows/dockwidgets example from Qt v5.x, originating from PyQt"""
+"""PySide6 port of the widgets/mainwindows/dockwidgets example from Qt v5.x,
+   originating from PyQt"""
 
 import sys
 
 from PySide6.QtCore import QDate, QFile, Qt, QTextStream
 from PySide6.QtGui import (QAction, QFont, QIcon, QKeySequence,
-        QTextCharFormat, QTextCursor, QTextTableFormat)
+                           QTextCharFormat, QTextCursor, QTextTableFormat)
 from PySide6.QtPrintSupport import QPrintDialog, QPrinter
 from PySide6.QtWidgets import (QApplication, QDialog, QDockWidget,
-        QFileDialog, QListWidget, QMainWindow, QMessageBox, QTextEdit)
+                               QFileDialog, QListWidget, QMainWindow,
+                               QMessageBox, QTextEdit)
 
-import dockwidgets_rc
+import dockwidgets_rc  # noqa: F401
 
 
 class MainWindow(QMainWindow):
@@ -62,8 +64,7 @@ class MainWindow(QMainWindow):
         cursor.insertBlock()
         cursor.insertText("Some Country")
         cursor.setPosition(top_frame.lastPosition())
-        cursor.insertText(QDate.currentDate().toString("d MMMM yyyy"),
-                text_format)
+        cursor.insertText(QDate.currentDate().toString("d MMMM yyyy"), text_format)
         cursor.insertBlock()
         cursor.insertBlock()
         cursor.insertText("Dear ", text_format)
@@ -103,7 +104,7 @@ class MainWindow(QMainWindow):
         if not file.open(QFile.WriteOnly | QFile.Text):
             reason = file.errorString()
             QMessageBox.warning(self, "Dock Widgets",
-                    f"Cannot write file {filename}:\n{reason}.")
+                                f"Cannot write file {filename}:\n{reason}.")
             return
 
         out = QTextStream(file)
@@ -143,8 +144,8 @@ class MainWindow(QMainWindow):
         if cursor.isNull():
             return
         cursor.beginEditBlock()
-        cursor.movePosition(QTextCursor.PreviousBlock, QTextCursor.MoveAnchor,
-                2)
+        cursor.movePosition(QTextCursor.PreviousBlock,
+                            QTextCursor.MoveAnchor, 2)
         cursor.insertBlock()
         cursor.insertText(paragraph)
         cursor.insertBlock()
@@ -152,43 +153,44 @@ class MainWindow(QMainWindow):
 
     def about(self):
         QMessageBox.about(self, "About Dock Widgets",
-                "The <b>Dock Widgets</b> example demonstrates how to use "
-                "Qt's dock widgets. You can enter your own text, click a "
-                "customer to add a customer name and address, and click "
-                "standard paragraphs to add them.")
+                          "The <b>Dock Widgets</b> example demonstrates how to use "
+                          "Qt's dock widgets. You can enter your own text, click a "
+                          "customer to add a customer name and address, and click "
+                          "standard paragraphs to add them.")
 
     def create_actions(self):
         icon = QIcon.fromTheme('document-new', QIcon(':/images/new.png'))
         self._new_letter_act = QAction(icon, "&New Letter",
-                self, shortcut=QKeySequence.New,
-                statusTip="Create a new form letter", triggered=self.new_letter)
+                                       self, shortcut=QKeySequence.New,
+                                       statusTip="Create a new form letter",
+                                       triggered=self.new_letter)
 
         icon = QIcon.fromTheme('document-save', QIcon(':/images/save.png'))
         self._save_act = QAction(icon, "&Save...", self,
-                shortcut=QKeySequence.Save,
-                statusTip="Save the current form letter", triggered=self.save)
+                                 shortcut=QKeySequence.Save,
+                                 statusTip="Save the current form letter", triggered=self.save)
 
         icon = QIcon.fromTheme('document-print', QIcon(':/images/print.png'))
         self._print_act = QAction(icon, "&Print...", self,
-                shortcut=QKeySequence.Print,
-                statusTip="Print the current form letter",
-                triggered=self.print_)
+                                  shortcut=QKeySequence.Print,
+                                  statusTip="Print the current form letter",
+                                  triggered=self.print_)
 
         icon = QIcon.fromTheme('edit-undo', QIcon(':/images/undo.png'))
         self._undo_act = QAction(icon, "&Undo", self,
-                shortcut=QKeySequence.Undo,
-                statusTip="Undo the last editing action", triggered=self.undo)
+                                 shortcut=QKeySequence.Undo,
+                                 statusTip="Undo the last editing action", triggered=self.undo)
 
         self._quit_act = QAction("&Quit", self, shortcut="Ctrl+Q",
-                statusTip="Quit the application", triggered=self.close)
+                                 statusTip="Quit the application", triggered=self.close)
 
         self._about_act = QAction("&About", self,
-                statusTip="Show the application's About box",
-                triggered=self.about)
+                                  statusTip="Show the application's About box",
+                                  triggered=self.about)
 
         self._about_qt_act = QAction("About &Qt", self,
-                statusTip="Show the Qt library's About box",
-                triggered=QApplication.instance().aboutQt)
+                                     statusTip="Show the Qt library's About box",
+                                     triggered=QApplication.instance().aboutQt)
 
     def create_menus(self):
         self._file_menu = self.menuBar().addMenu("&File")
@@ -241,21 +243,21 @@ class MainWindow(QMainWindow):
         self._paragraphs_list.addItems((
             "Thank you for your payment which we have received today.",
             "Your order has been dispatched and should be with you within "
-                "28 days.",
+            "28 days.",
             "We have dispatched those items that were in stock. The rest of "
-                "your order will be dispatched once all the remaining items "
-                "have arrived at our warehouse. No additional shipping "
-                "charges will be made.",
+            "your order will be dispatched once all the remaining items "
+            "have arrived at our warehouse. No additional shipping "
+            "charges will be made.",
             "You made a small overpayment (less than $5) which we will keep "
-                "on account for you, or return at your request.",
+            "on account for you, or return at your request.",
             "You made a small underpayment (less than $1), but we have sent "
-                "your order anyway. We'll add this underpayment to your next "
-                "bill.",
+            "your order anyway. We'll add this underpayment to your next "
+            "bill.",
             "Unfortunately you did not send enough money. Please remit an "
-                "additional $. Your order will be dispatched as soon as the "
-                "complete amount has been received.",
+            "additional $. Your order will be dispatched as soon as the "
+            "complete amount has been received.",
             "You made an overpayment (more than $5). Do you wish to buy more "
-                "items, or should we return the excess to you?"))
+            "items, or should we return the excess to you?"))
         dock.setWidget(self._paragraphs_list)
         self.addDockWidget(Qt.RightDockWidgetArea, dock)
         self._view_menu.addAction(dock.toggleViewAction())
index 927a1daa7c46abaf8c9f49e46cb959d1c5414565..92c0c9c2b7af423fa83637d134534f33d9a4fda6 100644 (file)
@@ -9,12 +9,12 @@ from functools import partial
 import sys
 
 from PySide6.QtCore import (QByteArray, QFile, QFileInfo, QSettings,
-        QSaveFile, QTextStream, Qt, Slot)
+                            QSaveFile, QTextStream, Qt, Slot)
 from PySide6.QtGui import QAction, QIcon, QKeySequence
 from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow,
-        QMdiArea, QMessageBox, QTextEdit)
+                               QMdiArea, QMessageBox, QTextEdit)
 
-import PySide6.QtExampleIcons
+import PySide6.QtExampleIcons  # noqa: F401
 
 
 class MdiChild(QTextEdit):
@@ -106,7 +106,7 @@ class MdiChild(QTextEdit):
             f = self.user_friendly_current_file()
             message = f"'{f}' has been modified.\nDo you want to save your changes?"
             ret = QMessageBox.warning(self, "MDI", message,
-                    QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
+                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
 
             if ret == QMessageBox.Save:
                 return self.save()
@@ -208,8 +208,8 @@ class MainWindow(QMainWindow):
     @Slot()
     def about(self):
         QMessageBox.about(self, "About MDI",
-                "The <b>MDI</b> example demonstrates how to write multiple "
-                "document interface applications using Qt.")
+                          "The <b>MDI</b> example demonstrates how to write multiple "
+                          "document interface applications using Qt.")
 
     @Slot()
     def update_menus(self):
@@ -225,8 +225,8 @@ class MainWindow(QMainWindow):
         self._previous_act.setEnabled(has_mdi_child)
         self._separator_act.setVisible(has_mdi_child)
 
-        has_selection = (self.active_mdi_child() is not None and
-                        self.active_mdi_child().textCursor().hasSelection())
+        has_selection = (self.active_mdi_child() is not None
+                         and self.active_mdi_child().textCursor().hasSelection())
         self._cut_act.setEnabled(has_selection)
         self._copy_act.setEnabled(has_selection)
 
@@ -273,80 +273,81 @@ class MainWindow(QMainWindow):
 
         icon = QIcon.fromTheme("document-new")
         self._new_act = QAction(icon, "&New", self,
-                shortcut=QKeySequence.New, statusTip="Create a new file",
-                triggered=self.new_file)
+                                shortcut=QKeySequence.New, statusTip="Create a new file",
+                                triggered=self.new_file)
 
         icon = QIcon.fromTheme("document-open")
         self._open_act = QAction(icon, "&Open...", self,
-                shortcut=QKeySequence.Open, statusTip="Open an existing file",
-                triggered=self.open)
+                                 shortcut=QKeySequence.Open, statusTip="Open an existing file",
+                                 triggered=self.open)
 
         icon = QIcon.fromTheme("document-save")
         self._save_act = QAction(icon, "&Save", self,
-                shortcut=QKeySequence.Save,
-                statusTip="Save the document to disk", triggered=self.save)
+                                 shortcut=QKeySequence.Save,
+                                 statusTip="Save the document to disk", triggered=self.save)
 
         self._save_as_act = QAction("Save &As...", self,
-                shortcut=QKeySequence.SaveAs,
-                statusTip="Save the document under a new name",
-                triggered=self.save_as)
+                                    shortcut=QKeySequence.SaveAs,
+                                    statusTip="Save the document under a new name",
+                                    triggered=self.save_as)
 
         self._exit_act = QAction("E&xit", self, shortcut=QKeySequence.Quit,
-                statusTip="Exit the application",
-                triggered=QApplication.instance().closeAllWindows)
+                                 statusTip="Exit the application",
+                                 triggered=QApplication.instance().closeAllWindows)
 
         icon = QIcon.fromTheme("edit-cut")
         self._cut_act = QAction(icon, "Cu&t", self,
-                shortcut=QKeySequence.Cut,
-                statusTip="Cut the current selection's contents to the clipboard",
-                triggered=self.cut)
+                                shortcut=QKeySequence.Cut,
+                                statusTip="Cut the current selection's contents to the clipboard",
+                                triggered=self.cut)
 
         icon = QIcon.fromTheme("edit-copy")
         self._copy_act = QAction(icon, "&Copy", self,
-                shortcut=QKeySequence.Copy,
-                statusTip="Copy the current selection's contents to the clipboard",
-                triggered=self.copy)
+                                 shortcut=QKeySequence.Copy,
+                                 statusTip="Copy the current selection's contents to the clipboard",
+                                 triggered=self.copy)
 
         icon = QIcon.fromTheme("edit-paste")
         self._paste_act = QAction(icon, "&Paste", self,
-                shortcut=QKeySequence.Paste,
-                statusTip="Paste the clipboard's contents into the current selection",
-                triggered=self.paste)
+                                  shortcut=QKeySequence.Paste,
+                                  statusTip="Paste the clipboard's contents into the current "
+                                            "selection",
+                                  triggered=self.paste)
 
         self._close_act = QAction("Cl&ose", self,
-                statusTip="Close the active window",
-                triggered=self._mdi_area.closeActiveSubWindow)
+                                  statusTip="Close the active window",
+                                  triggered=self._mdi_area.closeActiveSubWindow)
 
         self._close_all_act = QAction("Close &All", self,
-                statusTip="Close all the windows",
-                triggered=self._mdi_area.closeAllSubWindows)
+                                      statusTip="Close all the windows",
+                                      triggered=self._mdi_area.closeAllSubWindows)
 
         self._tile_act = QAction("&Tile", self, statusTip="Tile the windows",
-                triggered=self._mdi_area.tileSubWindows)
+                                 triggered=self._mdi_area.tileSubWindows)
 
         self._cascade_act = QAction("&Cascade", self,
-                statusTip="Cascade the windows",
-                triggered=self._mdi_area.cascadeSubWindows)
+                                    statusTip="Cascade the windows",
+                                    triggered=self._mdi_area.cascadeSubWindows)
 
         self._next_act = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild,
-                statusTip="Move the focus to the next window",
-                triggered=self._mdi_area.activateNextSubWindow)
+                                 statusTip="Move the focus to the next window",
+                                 triggered=self._mdi_area.activateNextSubWindow)
 
         self._previous_act = QAction("Pre&vious", self,
-                shortcut=QKeySequence.PreviousChild,
-                statusTip="Move the focus to the previous window",
-                triggered=self._mdi_area.activatePreviousSubWindow)
+                                     shortcut=QKeySequence.PreviousChild,
+                                     statusTip="Move the focus to the previous window",
+                                     triggered=self._mdi_area.activatePreviousSubWindow)
 
         self._separator_act = QAction(self)
         self._separator_act.setSeparator(True)
 
         self._about_act = QAction("&About", self,
-                statusTip="Show the application's About box",
-                triggered=self.about)
+                                  statusTip="Show the application's About box",
+                                  triggered=self.about)
 
         self._about_qt_act = QAction("About &Qt", self,
-                statusTip="Show the Qt library's About box",
-                triggered=QApplication.instance().aboutQt)
+                                     statusTip="Show the Qt library's About box",
+                                     triggered=QApplication.instance().aboutQt)
 
     def create_menus(self):
         self._file_menu = self.menuBar().addMenu("&File")
@@ -435,7 +436,7 @@ if __name__ == '__main__':
 
     icon_paths = QIcon.themeSearchPaths()
     QIcon.setThemeSearchPaths(icon_paths + [":/qt-project.org/icons"])
-    QIcon.setFallbackThemeName("example_icons");
+    QIcon.setFallbackThemeName("example_icons")
 
     main_win = MainWindow()
     for f in options.files:
index c1f2a5b0c785a9463e47d5d0ef32240d02c566b9..858a8cd9fe7f07561c3a39f436cbe246bea0c8a6 100644 (file)
@@ -6,11 +6,12 @@
 
 from PySide6.QtCore import QPoint, QRect, QSize, Qt, qVersion
 from PySide6.QtGui import (QBrush, QConicalGradient, QLinearGradient, QPainter,
-        QPainterPath, QPalette, QPen, QPixmap, QPolygon, QRadialGradient)
+                           QPainterPath, QPalette, QPen, QPixmap, QPolygon,
+                           QRadialGradient)
 from PySide6.QtWidgets import (QApplication, QCheckBox, QComboBox, QGridLayout,
-        QLabel, QSpinBox, QWidget)
+                               QLabel, QSpinBox, QWidget)
 
-import basicdrawing_rc
+import basicdrawing_rc  # noqa: F401
 
 
 class RenderArea(QWidget):
@@ -190,12 +191,9 @@ class Window(QWidget):
         pen_join_label.setBuddy(self._pen_join_combo_box)
 
         self._brush_style_combo_box = QComboBox()
-        self._brush_style_combo_box.addItem("Linear Gradient",
-                Qt.LinearGradientPattern)
-        self._brush_style_combo_box.addItem("Radial Gradient",
-                Qt.RadialGradientPattern)
-        self._brush_style_combo_box.addItem("Conical Gradient",
-                Qt.ConicalGradientPattern)
+        self._brush_style_combo_box.addItem("Linear Gradient", Qt.LinearGradientPattern)
+        self._brush_style_combo_box.addItem("Radial Gradient", Qt.RadialGradientPattern)
+        self._brush_style_combo_box.addItem("Conical Gradient", Qt.ConicalGradientPattern)
         self._brush_style_combo_box.addItem("Texture", Qt.TexturePattern)
         self._brush_style_combo_box.addItem("Solid", Qt.SolidPattern)
         self._brush_style_combo_box.addItem("Horizontal", Qt.HorPattern)
@@ -260,24 +258,23 @@ class Window(QWidget):
         self.setWindowTitle("Basic Drawing")
 
     def shape_changed(self):
-        shape = self._shape_combo_box.itemData(self._shape_combo_box.currentIndex(),
-                id_role)
+        shape = self._shape_combo_box.itemData(self._shape_combo_box.currentIndex(), id_role)
         self._render_area.set_shape(shape)
 
     def pen_changed(self):
         width = self._pen_width_spin_box.value()
         style = Qt.PenStyle(self._pen_style_combo_box.itemData(
-                self._pen_style_combo_box.currentIndex(), id_role))
+            self._pen_style_combo_box.currentIndex(), id_role))
         cap = Qt.PenCapStyle(self._pen_cap_combo_box.itemData(
-                self._pen_cap_combo_box.currentIndex(), id_role))
+            self._pen_cap_combo_box.currentIndex(), id_role))
         join = Qt.PenJoinStyle(self._pen_join_combo_box.itemData(
-                self._pen_join_combo_box.currentIndex(), id_role))
+            self._pen_join_combo_box.currentIndex(), id_role))
 
         self._render_area.set_pen(QPen(Qt.blue, width, style, cap, join))
 
     def brush_changed(self):
         style = Qt.BrushStyle(self._brush_style_combo_box.itemData(
-                self._brush_style_combo_box.currentIndex(), id_role))
+            self._brush_style_combo_box.currentIndex(), id_role))
 
         if style == Qt.LinearGradientPattern:
             linear_gradient = QLinearGradient(0, 0, 100, 100)
index 0edc9cb9f673301ff102dcc573f903625f085d9e..d2c60178f103cc4b3fe1afec090ae5d65843771a 100644 (file)
@@ -2,12 +2,13 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
 
-"""PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating from PyQt"""
+"""PySide6 port of the widgets/painting/concentriccircles example from Qt v5.x, originating
+   from PyQt"""
 
 from PySide6.QtCore import QRect, QRectF, QSize, Qt, QTimer
 from PySide6.QtGui import QColor, QPainter, QPalette, QPen
 from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout, QLabel,
-        QSizePolicy, QWidget)
+                               QSizePolicy, QWidget)
 
 
 class CircleWidget(QWidget):
@@ -52,10 +53,10 @@ class CircleWidget(QWidget):
 
                     if self._float_based:
                         painter.drawEllipse(QRectF(-diameter / 2.0,
-                                -diameter / 2.0, diameter, diameter))
+                                                   -diameter / 2.0, diameter, diameter))
                     else:
                         painter.drawEllipse(QRect(-diameter / 2,
-                                -diameter / 2, diameter, diameter))
+                                                  -diameter / 2, diameter, diameter))
 
 
 class Window(QWidget):
index 58584baff921cad2f0abdba515b3012662e5bdb2..2ca078ad913fb6a148eb676ff05fed0c9d744a3e 100644 (file)
@@ -116,15 +116,17 @@ class MainWindow(QMainWindow):
         self.bar = self.addToolBar("Menu")
         self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
         self._save_action = self.bar.addAction(
-            qApp.style().standardIcon(QStyle.SP_DialogSaveButton), "Save", self.on_save
+            qApp.style().standardIcon(QStyle.SP_DialogSaveButton),  # noqa: F821
+            "Save", self.on_save
         )
         self._save_action.setShortcut(QKeySequence.Save)
         self._open_action = self.bar.addAction(
-            qApp.style().standardIcon(QStyle.SP_DialogOpenButton), "Open", self.on_open
+            qApp.style().standardIcon(QStyle.SP_DialogOpenButton),  # noqa: F821
+            "Open", self.on_open
         )
         self._open_action.setShortcut(QKeySequence.Open)
         self.bar.addAction(
-            qApp.style().standardIcon(QStyle.SP_DialogResetButton),
+            qApp.style().standardIcon(QStyle.SP_DialogResetButton),  # noqa: F821
             "Clear",
             self.painter_widget.clear,
         )
index fd0be01fe0d7fd5ec9fa3423144928eb26521f4e..9725624c399c0406ffd845fbdf18388c0b03f956 100644 (file)
@@ -89,8 +89,7 @@ class MainWindow(QMainWindow):
         body_frame_format.setWidth(QTextLength(QTextLength.PercentageLength, 100))
         cursor.insertFrame(body_frame_format)
 
-        cursor.insertText("I would like to place an order for the following "
-                "items:", text_format)
+        cursor.insertText("I would like to place an order for the following items:", text_format)
         cursor.insertBlock()
         cursor.insertBlock()
 
@@ -121,17 +120,17 @@ class MainWindow(QMainWindow):
         cursor.insertBlock()
 
         cursor.insertText("Please update my records to take account of the "
-                "following privacy information:")
+                          "following privacy information:")
         cursor.insertBlock()
 
         offers_table = cursor.insertTable(2, 2)
 
         cursor = offers_table.cellAt(0, 1).firstCursorPosition()
         cursor.insertText("I want to receive more information about your "
-                "company's products and special offers.", text_format)
+                          "company's products and special offers.", text_format)
         cursor = offers_table.cellAt(1, 1).firstCursorPosition()
         cursor.insertText("I do not want to receive any promotional "
-                "information from your company.", text_format)
+                          "information from your company.", text_format)
 
         if sendOffers:
             cursor = offers_table.cellAt(0, 0).firstCursorPosition()
@@ -153,8 +152,8 @@ class MainWindow(QMainWindow):
     def create_sample(self):
         dialog = DetailsDialog('Dialog with default values', self)
         self.create_letter('Mr Smith',
-                '12 High Street\nSmall Town\nThis country',
-                dialog.order_items(), True)
+                           '12 High Street\nSmall Town\nThis country',
+                           dialog.order_items(), True)
 
     @Slot()
     def open_dialog(self):
@@ -162,7 +161,7 @@ class MainWindow(QMainWindow):
 
         if dialog.exec() == QDialog.Accepted:
             self.create_letter(dialog.sender_name(), dialog.sender_address(),
-                    dialog.order_items(), dialog.send_offers())
+                               dialog.order_items(), dialog.send_offers())
 
     @Slot()
     def print_file(self):
@@ -193,8 +192,7 @@ class DetailsDialog(QDialog):
 
         self._name_edit = QLineEdit()
         self._address_edit = QTextEdit()
-        self._offers_check_box = QCheckBox("Send information about "
-                "products and special offers:")
+        self._offers_check_box = QCheckBox("Send information about products and special offers:")
 
         self.setup_items_table()
 
@@ -250,9 +248,9 @@ class DetailsDialog(QDialog):
             return
 
         answer = QMessageBox.warning(self, "Incomplete Form",
-                "The form does not contain all the necessary information.\n"
-                "Do you want to discard it?",
-                QMessageBox.Yes, QMessageBox.No)
+                                     "The form does not contain all the necessary information.\n"
+                                     "Do you want to discard it?",
+                                     QMessageBox.Yes, QMessageBox.No)
 
         if answer == QMessageBox.Yes:
             self.reject()
index c329eefd669bec8836179884f10d51fa5887b9c0..021328977ff725ab70c13504eedbc6313dc50283 100644 (file)
@@ -12,7 +12,7 @@ from PySide6.QtCore import (QFile, Qt, QTextStream)
 from PySide6.QtGui import (QColor, QFont, QFontDatabase, QKeySequence,
                            QSyntaxHighlighter, QTextCharFormat)
 from PySide6.QtWidgets import (QApplication, QFileDialog, QMainWindow,
-    QPlainTextEdit)
+                               QPlainTextEdit)
 
 
 class MainWindow(QMainWindow):
@@ -81,7 +81,7 @@ class MainWindow(QMainWindow):
         quit_act.triggered.connect(self.close)
 
         help_menu = self.menuBar().addMenu("&Help")
-        help_menu.addAction("About &Qt", qApp.aboutQt)
+        help_menu.addAction("About &Qt", qApp.aboutQt)  # noqa: F821
 
 
 class Highlighter(QSyntaxHighlighter):
index 88b679edcfb6f45f88d107175351cbefae5e2742..330ea5fc5863186d3f4e3520e5764c5ff08fcd6d 100644 (file)
@@ -11,7 +11,7 @@ from PySide6.QtWidgets import QApplication
 
 from textedit import TextEdit
 
-import textedit_rc
+import textedit_rc  # noqa: F401
 
 
 if __name__ == '__main__':
index 4f4146d3478b44449507df820f1da19b86fbc720..a3940ae0ff42b708b294dcb1fe044be28932babb 100644 (file)
@@ -55,7 +55,7 @@ class TextEdit(QMainWindow):
 
         help_menu = self.menuBar().addMenu("Help")
         help_menu.addAction("About", self.about)
-        help_menu.addAction("About &Qt", qApp.aboutQt)
+        help_menu.addAction("About &Qt", qApp.aboutQt)  # noqa: F821
 
         text_font = QFont("Helvetica")
         text_font.setStyleHint(QFont.SansSerif)
@@ -621,7 +621,7 @@ class TextEdit(QMainWindow):
             above = QTextCursor(cursor)
             above.movePosition(QTextCursor.Up)
             if (above.currentList()
-                and list_fmt.indent() + amount == above.currentList().format().indent()):
+                    and list_fmt.indent() + amount == above.currentList().format().indent()):
                 above.currentList().add(cursor.block())
             else:
                 list_fmt.setIndent(list_fmt.indent() + amount)
index 4fc9c62a29e62c2801e282c6e0fa532edde71017..2d2bb2bb712958fc83d68b27bc0779aea466f74a 100644 (file)
@@ -5,12 +5,14 @@ import re
 import logging
 
 from PySide6.QtCore import (QMargins, QRegularExpression, QRegularExpressionMatch,
-    QRegularExpressionMatchIterator, Qt, Slot,)
+                            QRegularExpressionMatchIterator, Qt, Slot,)
 from PySide6.QtGui import (QAction, QColor, QContextMenuEvent, QFontDatabase,
-    QGuiApplication, QIcon, QPalette,)
+                           QGuiApplication, QIcon, QPalette,)
 from PySide6.QtWidgets import (QCheckBox, QComboBox, QDialog, QFormLayout,
-    QFrame, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QPlainTextEdit,
-    QSpinBox, QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget,)
+                               QFrame, QGridLayout, QHBoxLayout, QLabel,
+                               QLineEdit, QPlainTextEdit, QSpinBox,
+                               QTreeWidget, QTreeWidgetItem, QVBoxLayout,
+                               QWidget)
 
 
 def rawStringLiteral(pattern: str) -> str:
@@ -42,7 +44,7 @@ def codeToPattern(code: str) -> str:
         if code_characters[index] == '\\':
             del code_characters[index]
             code_characters_size -= 1
-        index +=1
+        index += 1
     code = "".join(code_characters)
 
     if code.startswith('"') and code.endswith('"'):
@@ -85,7 +87,7 @@ class PatternLineEdit(QLineEdit):
             t = (
                 t[: selection_start]
                 + escapedSelection
-                + t[selection_start + len(selection) :]
+                + t[selection_start + len(selection):]
             )
             self.setText(t)
 
@@ -327,23 +329,20 @@ class RegularExpressionDialog(QDialog):
         self.patternOptionsCheckBoxLayout = QGridLayout()
         gridRow = 0
 
-        self.patternOptionsCheckBoxLayout.addWidget(self.caseInsensitiveOptionCheckBox, gridRow, \
-        1)
-        self.patternOptionsCheckBoxLayout.addWidget(self.dotMatchesEverythingOptionCheckBox, gridRow\
-        ,2)
+        self.patternOptionsCheckBoxLayout.addWidget(self.caseInsensitiveOptionCheckBox, gridRow, 1)
+        self.patternOptionsCheckBoxLayout.addWidget(
+            self.dotMatchesEverythingOptionCheckBox, gridRow, 2)
         gridRow = gridRow + 1
-        self.patternOptionsCheckBoxLayout.addWidget(self.multilineOptionCheckBox, gridRow, \
-        1)
-        self.patternOptionsCheckBoxLayout.addWidget(self.extendedPatternSyntaxOptionCheckBox, gridRow \
-        , 2)
+        self.patternOptionsCheckBoxLayout.addWidget(self.multilineOptionCheckBox, gridRow, 1)
+        self.patternOptionsCheckBoxLayout.addWidget(
+            self.extendedPatternSyntaxOptionCheckBox, gridRow, 2)
         gridRow = gridRow + 1
-        self.patternOptionsCheckBoxLayout.addWidget(self.invertedGreedinessOptionCheckBox, gridRow,\
-        1)
-        self.patternOptionsCheckBoxLayout.addWidget(self.dontCaptureOptionCheckBox, gridRow,\
-        2)
+        self.patternOptionsCheckBoxLayout.addWidget(
+            self.invertedGreedinessOptionCheckBox, gridRow, 1)
+        self.patternOptionsCheckBoxLayout.addWidget(self.dontCaptureOptionCheckBox, gridRow, 2)
         gridRow = gridRow + 1
-        self.patternOptionsCheckBoxLayout.addWidget(self.useUnicodePropertiesOptionCheckBox, gridRow,\
-        1)
+        self.patternOptionsCheckBoxLayout.addWidget(
+            self.useUnicodePropertiesOptionCheckBox, gridRow, 1)
 
         form_layout.addRow("Pattern options:", self.patternOptionsCheckBoxLayout)
 
index 89c8130069a64add0da967f641472b3b46eca9bd..3c0eb451d303146eca4c096d8778bde31080c3e2 100644 (file)
@@ -102,17 +102,16 @@ class AddressBook(QWidget):
         address = self._address_text.toPlainText()
 
         if name == "" or address == "":
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name and address.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
             return
 
         if name not in self.contacts:
             self.contacts[name] = address
             QMessageBox.information(self, "Add Successful",
-                    f'"{name}" has been added to your address book.')
+                                    f'"{name}" has been added to your address book.')
         else:
             QMessageBox.information(self, "Add Unsuccessful",
-                    f'Sorry, "{name}" is already in your address book.')
+                                    f'Sorry, "{name}" is already in your address book.')
             return
 
         if not self.contacts:
index 571a96a4818995b4ee71400469ab675dd9c0f047..611796f5e2fefdd4408996d34811c0f424352a3b 100644 (file)
@@ -116,17 +116,16 @@ class AddressBook(QWidget):
         address = self._address_text.toPlainText()
 
         if name == "" or address == "":
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name and address.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
             return
 
         if name not in self.contacts:
             self.contacts[name] = address
             QMessageBox.information(self, "Add Successful",
-                    f'"{name}" has been added to your address book.')
+                                    f'"{name}" has been added to your address book.')
         else:
             QMessageBox.information(self, "Add Unsuccessful",
-                    f'Sorry, "{name}" is already in your address book.')
+                                    f'Sorry, "{name}" is already in your address book.')
             return
 
         if not self.contacts:
index 505fe4db9b6746253db7b08281b396f9daae242a..95f31d46cf29348ae04a7091d16bc548b9c0fb6f 100644 (file)
@@ -128,34 +128,34 @@ class AddressBook(QWidget):
         address = self._address_text.toPlainText()
 
         if name == "" or address == "":
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name and address.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
             return
 
         if self._current_mode == self.AddingMode:
             if name not in self.contacts:
                 self.contacts[name] = address
                 QMessageBox.information(self, "Add Successful",
-                        f'"{name}" has been added to your address book.')
+                                        f'"{name}" has been added to your address book.')
             else:
                 QMessageBox.information(self, "Add Unsuccessful",
-                        f'Sorry, "{name}" is already in your address book.')
+                                        f'Sorry, "{name}" is already in your address book.')
                 return
 
         elif self._current_mode == self.EditingMode:
             if self._old_name != name:
                 if name not in self.contacts:
                     QMessageBox.information(self, "Edit Successful",
-                            f'"{self.oldName}" has been edited in your address book.')
+                                            f'"{self.oldName}" has been edited in your '
+                                            'address book.')
                     del self.contacts[self._old_name]
                     self.contacts[name] = address
                 else:
                     QMessageBox.information(self, "Edit Unsuccessful",
-                            f'Sorry, "{name}" is already in your address book.')
+                                            f'Sorry, "{name}" is already in your address book.')
                     return
             elif self._old_address != address:
                 QMessageBox.information(self, "Edit Successful",
-                        f'"{name}" has been edited in your address book.')
+                                        f'"{name}" has been edited in your address book.')
                 self.contacts[name] = address
 
         self.update_interface(self.NavigationMode)
@@ -169,19 +169,18 @@ class AddressBook(QWidget):
     @Slot()
     def remove_contact(self):
         name = self._name_line.text()
-        address = self._address_text.toPlainText()
 
         if name in self.contacts:
             button = QMessageBox.question(self, "Confirm Remove",
-                    f'Are you sure you want to remove "{name}"?',
-                    QMessageBox.Yes | QMessageBox.No)
+                                          f'Are you sure you want to remove "{name}"?',
+                                          QMessageBox.Yes | QMessageBox.No)
 
             if button == QMessageBox.Yes:
                 self.previous()
                 del self.contacts[name]
 
                 QMessageBox.information(self, "Remove Successful",
-                        f'"{name}" has been removed from your address book.')
+                                        f'"{name}" has been removed from your address book.')
 
         self.update_interface(self.NavigationMode)
 
index 72245703fc3b0952501afa724d532fc1feb6d9d7..1e9c05862cc352ddd13425b67b82a8f37eded978 100644 (file)
@@ -134,34 +134,34 @@ class AddressBook(QWidget):
         address = self._address_text.toPlainText()
 
         if name == "" or address == "":
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name and address.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
             return
 
         if self._current_mode == self.AddingMode:
             if name not in self.contacts:
                 self.contacts[name] = address
                 QMessageBox.information(self, "Add Successful",
-                        f'"{name}" has been added to your address book.')
+                                        f'"{name}" has been added to your address book.')
             else:
                 QMessageBox.information(self, "Add Unsuccessful",
-                        f'Sorry, "{name}" is already in your address book.')
+                                        f'Sorry, "{name}" is already in your address book.')
                 return
 
         elif self._current_mode == self.EditingMode:
             if self._old_name != name:
                 if name not in self.contacts:
                     QMessageBox.information(self, "Edit Successful",
-                            f'"{self.oldName}" has been edited in your address book.')
+                                            f'"{self.oldName}" has been edited in your '
+                                            'address book.')
                     del self.contacts[self._old_name]
                     self.contacts[name] = address
                 else:
                     QMessageBox.information(self, "Edit Unsuccessful",
-                            f'Sorry, "{name}" is already in your address book.')
+                                            f'Sorry, "{name}" is already in your address book.')
                     return
             elif self._old_address != address:
                 QMessageBox.information(self, "Edit Successful",
-                        f'"{name}" has been edited in your address book.')
+                                        f'"{name}" has been edited in your address book.')
                 self.contacts[name] = address
 
         self.update_interface(self.NavigationMode)
@@ -175,19 +175,18 @@ class AddressBook(QWidget):
     @Slot()
     def remove_contact(self):
         name = self._name_line.text()
-        address = self._address_text.toPlainText()
 
         if name in self.contacts:
             button = QMessageBox.question(self, "Confirm Remove",
-                    f'Are you sure you want to remove "{name}"?',
-                    QMessageBox.Yes | QMessageBox.No)
+                                          f'Are you sure you want to remove "{name}"?',
+                                          QMessageBox.Yes | QMessageBox.No)
 
             if button == QMessageBox.Yes:
                 self.previous()
                 del self.contacts[name]
 
                 QMessageBox.information(self, "Remove Successful",
-                        f'"{name}" has been removed from your address book.')
+                                        f'"{name}" has been removed from your address book.')
 
         self.update_interface(self.NavigationMode)
 
@@ -243,7 +242,7 @@ class AddressBook(QWidget):
                 self._address_text.setText(self.contacts[contact_name])
             else:
                 QMessageBox.information(self, "Contact Not Found",
-                        f'Sorry, "{contact_name}" is not in your address book.')
+                                        f'Sorry, "{contact_name}" is not in your address book.')
                 return
 
         self.update_interface(self.NavigationMode)
@@ -311,8 +310,7 @@ class FindDialog(QDialog):
         text = self._line_edit.text()
 
         if not text:
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name.")
             return
         else:
             self._find_text = text
index f75fbf44fff36c882b5b9070cbe6778336ed27e4..d11298fb9dcf0cf1efe2945bccc6441f74ed4d58 100644 (file)
@@ -145,34 +145,34 @@ class AddressBook(QWidget):
         address = self._address_text.toPlainText()
 
         if name == "" or address == "":
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name and address.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
             return
 
         if self._current_mode == self.AddingMode:
             if name not in self.contacts:
                 self.contacts[name] = address
                 QMessageBox.information(self, "Add Successful",
-                        f'"{name}" has been added to your address book.')
+                                        f'"{name}" has been added to your address book.')
             else:
                 QMessageBox.information(self, "Add Unsuccessful",
-                        f'Sorry, "{name}" is already in your address book.')
+                                        f'Sorry, "{name}" is already in your address book.')
                 return
 
         elif self._current_mode == self.EditingMode:
             if self._old_name != name:
                 if name not in self.contacts:
                     QMessageBox.information(self, "Edit Successful",
-                            f'"{self.oldName}" has been edited in your address book.')
+                                            f'"{self.oldName}" has been edited in your '
+                                            'address book.')
                     del self.contacts[self._old_name]
                     self.contacts[name] = address
                 else:
                     QMessageBox.information(self, "Edit Unsuccessful",
-                            f'Sorry, "{name}" is already in your address book.')
+                                            f'Sorry, "{name}" is already in your address book.')
                     return
             elif self._old_address != address:
                 QMessageBox.information(self, "Edit Successful",
-                        f'"{name}" has been edited in your address book.')
+                                        f'"{name}" has been edited in your address book.')
                 self.contacts[name] = address
 
         self.update_interface(self.NavigationMode)
@@ -186,19 +186,18 @@ class AddressBook(QWidget):
     @Slot()
     def remove_contact(self):
         name = self._name_line.text()
-        address = self._address_text.toPlainText()
 
         if name in self.contacts:
             button = QMessageBox.question(self, "Confirm Remove",
-                    f'Are you sure you want to remove "{name}"?',
-                    QMessageBox.Yes | QMessageBox.No)
+                                          f'Are you sure you want to remove "{name}"?',
+                                          QMessageBox.Yes | QMessageBox.No)
 
             if button == QMessageBox.Yes:
                 self.previous()
                 del self.contacts[name]
 
                 QMessageBox.information(self, "Remove Successful",
-                        f'"{name}" has been removed from your address book.')
+                                        f'"{name}" has been removed from your address book.')
 
         self.update_interface(self.NavigationMode)
 
@@ -254,7 +253,7 @@ class AddressBook(QWidget):
                 self._address_text.setText(self.contacts[contact_name])
             else:
                 QMessageBox.information(self, "Contact Not Found",
-                        f'Sorry, "{contact_name}" is not in your address book.')
+                                        f'Sorry, "{contact_name}" is not in your address book.')
                 return
 
         self.update_interface(self.NavigationMode)
@@ -304,8 +303,8 @@ class AddressBook(QWidget):
 
     def save_to_file(self):
         fileName, _ = QFileDialog.getSaveFileName(self,
-                "Save Address Book", '',
-                "Address Book (*.abk);;All Files (*)")
+                                                  "Save Address Book", '',
+                                                  "Address Book (*.abk);;All Files (*)")
 
         if not fileName:
             return
@@ -314,7 +313,7 @@ class AddressBook(QWidget):
             out_file = open(str(fileName), 'wb')
         except IOError:
             QMessageBox.information(self, "Unable to open file",
-                    f'There was an error opening "{fileName}"')
+                                    f'There was an error opening "{fileName}"')
             return
 
         pickle.dump(self.contacts, out_file)
@@ -322,8 +321,8 @@ class AddressBook(QWidget):
 
     def load_from_file(self):
         fileName, _ = QFileDialog.getOpenFileName(self,
-                "Open Address Book", '',
-                "Address Book (*.abk);;All Files (*)")
+                                                  "Open Address Book", '',
+                                                  "Address Book (*.abk);;All Files (*)")
 
         if not fileName:
             return
@@ -332,7 +331,7 @@ class AddressBook(QWidget):
             in_file = open(str(fileName), 'rb')
         except IOError:
             QMessageBox.information(self, "Unable to open file",
-                    f'There was an error opening "{fileName}"')
+                                    f'There was an error opening "{fileName}"')
             return
 
         self.contacts = pickle.load(in_file)
@@ -340,8 +339,7 @@ class AddressBook(QWidget):
 
         if len(self.contacts) == 0:
             QMessageBox.information(self, "No contacts in file",
-                    "The file you are attempting to open contains no "
-                    "contacts.")
+                                    "The file you are attempting to open contains no contacts.")
         else:
             for name, address in self.contacts:
                 self._name_line.setText(name)
@@ -375,8 +373,7 @@ class FindDialog(QDialog):
         text = self._line_edit.text()
 
         if not text:
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name.")
             return
 
         self._find_text = text
index 2f874f9bd836037fa6719b597b9e02cd7713b87b..3829c003dfa6276bab8e789c35569bfbdcfa3a49 100644 (file)
@@ -151,34 +151,34 @@ class AddressBook(QWidget):
         address = self._address_text.toPlainText()
 
         if name == "" or address == "":
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name and address.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name and address.")
             return
 
         if self._current_mode == self.AddingMode:
             if name not in self.contacts:
                 self.contacts[name] = address
                 QMessageBox.information(self, "Add Successful",
-                        f'"{name}" has been added to your address book.')
+                                        f'"{name}" has been added to your address book.')
             else:
                 QMessageBox.information(self, "Add Unsuccessful",
-                        f'Sorry, "{name}" is already in your address book.')
+                                        f'Sorry, "{name}" is already in your address book.')
                 return
 
         elif self._current_mode == self.EditingMode:
             if self._old_name != name:
                 if name not in self.contacts:
                     QMessageBox.information(self, "Edit Successful",
-                            f'"{self.oldName}" has been edited in your address book.')
+                                            f'"{self.oldName}" has been edited in your '
+                                            'address book.')
                     del self.contacts[self._old_name]
                     self.contacts[name] = address
                 else:
                     QMessageBox.information(self, "Edit Unsuccessful",
-                            f'Sorry, "{name}" is already in your address book.')
+                                            f'Sorry, "{name}" is already in your address book.')
                     return
             elif self._old_address != address:
                 QMessageBox.information(self, "Edit Successful",
-                        f'"{name}" has been edited in your address book.')
+                                        f'"{name}" has been edited in your address book.')
                 self.contacts[name] = address
 
         self.update_interface(self.NavigationMode)
@@ -192,19 +192,18 @@ class AddressBook(QWidget):
     @Slot()
     def remove_contact(self):
         name = self._name_line.text()
-        address = self._address_text.toPlainText()
 
         if name in self.contacts:
             button = QMessageBox.question(self, "Confirm Remove",
-                    f'Are you sure you want to remove "{name}"?',
-                    QMessageBox.Yes | QMessageBox.No)
+                                          f'Are you sure you want to remove "{name}"?',
+                                          QMessageBox.Yes | QMessageBox.No)
 
             if button == QMessageBox.Yes:
                 self.previous()
                 del self.contacts[name]
 
                 QMessageBox.information(self, "Remove Successful",
-                        f'"{name}" has been removed from your address book.')
+                                        f'"{name}" has been removed from your address book.')
 
         self.update_interface(self.NavigationMode)
 
@@ -260,7 +259,7 @@ class AddressBook(QWidget):
                 self._address_text.setText(self.contacts[contact_name])
             else:
                 QMessageBox.information(self, "Contact Not Found",
-                        f'Sorry, "{contact_name}" is not in your address book.')
+                                        f'Sorry, "{contact_name}" is not in your address book.')
                 return
 
         self.update_interface(self.NavigationMode)
@@ -313,8 +312,8 @@ class AddressBook(QWidget):
 
     def save_to_file(self):
         fileName, _ = QFileDialog.getSaveFileName(self,
-                "Save Address Book", '',
-                "Address Book (*.abk);;All Files (*)")
+                                                  "Save Address Book", '',
+                                                  "Address Book (*.abk);;All Files (*)")
 
         if not fileName:
             return
@@ -323,7 +322,7 @@ class AddressBook(QWidget):
             out_file = open(str(fileName), 'wb')
         except IOError:
             QMessageBox.information(self, "Unable to open file",
-                    f'There was an error opening "{fileName}"')
+                                    f'There was an error opening "{fileName}"')
             return
 
         pickle.dump(self.contacts, out_file)
@@ -331,8 +330,8 @@ class AddressBook(QWidget):
 
     def load_from_file(self):
         fileName, _ = QFileDialog.getOpenFileName(self,
-                "Open Address Book", '',
-                "Address Book (*.abk);;All Files (*)")
+                                                  "Open Address Book", '',
+                                                  "Address Book (*.abk);;All Files (*)")
 
         if not fileName:
             return
@@ -341,7 +340,7 @@ class AddressBook(QWidget):
             in_file = open(str(fileName), 'rb')
         except IOError:
             QMessageBox.information(self, "Unable to open file",
-                    f'There was an error opening "{fileName}"')
+                                    f'There was an error opening "{fileName}"')
             return
 
         self.contacts = pickle.load(in_file)
@@ -349,8 +348,7 @@ class AddressBook(QWidget):
 
         if len(self.contacts) == 0:
             QMessageBox.information(self, "No contacts in file",
-                    "The file you are attempting to open contains no "
-                    "contacts.")
+                                    "The file you are attempting to open contains no contacts.")
         else:
             for name, address in self.contacts:
                 self._name_line.setText(name)
@@ -372,7 +370,7 @@ class AddressBook(QWidget):
             last_name = ''
 
         file_name = QFileDialog.getSaveFileName(self, "Export Contact",
-                '', "vCard Files (*.vcf);;All Files (*)")[0]
+                                                '', "vCard Files (*.vcf);;All Files (*)")[0]
 
         if not file_name:
             return
@@ -380,8 +378,7 @@ class AddressBook(QWidget):
         out_file = QFile(file_name)
 
         if not out_file.open(QIODevice.WriteOnly):
-            QMessageBox.information(self, "Unable to open file",
-                    out_file.errorString())
+            QMessageBox.information(self, "Unable to open file", out_file.errorString())
             return
 
         out_s = QTextStream(out_file)
@@ -399,7 +396,7 @@ class AddressBook(QWidget):
         out_s << 'END:VCARD' << '\n'
 
         QMessageBox.information(self, "Export Successful",
-                f'"{name}" has been exported as a vCard.')
+                                f'"{name}" has been exported as a vCard.')
 
 
 class FindDialog(QDialog):
@@ -427,8 +424,7 @@ class FindDialog(QDialog):
         text = self._line_edit.text()
 
         if not text:
-            QMessageBox.information(self, "Empty Field",
-                    "Please enter a name.")
+            QMessageBox.information(self, "Empty Field", "Please enter a name.")
             return
 
         self._find_text = text
index c9c3fa7f6faec9ba6a7d0f1c90a085910eba619a..8649bb5629b95a46f4b57dcd8b0a48bba4797245 100644 (file)
@@ -43,8 +43,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -113,7 +113,7 @@ class MyWidget(QWidget):
         quit = QPushButton("&Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange()
         angle.set_range(5, 70)
index 997eecbd1fe46439c4cc7189d0def5cced9eee17..fbfd2481d32504bf02fef8a56cf8a3b503f8ce56 100644 (file)
@@ -45,8 +45,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -175,7 +175,7 @@ class MyWidget(QWidget):
         quit = QPushButton("&Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange()
         angle.set_range(5, 70)
index 8847b2208e798f7f60137626db3e199522e652e5..749c246840eb12380b7bee4e386147119607de05 100644 (file)
@@ -62,8 +62,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -226,7 +226,7 @@ class MyWidget(QWidget):
         quit = QPushButton("&Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange("ANGLE")
         angle.set_range(5, 70)
index 4eb45a374006d5dca84257d507a29a7e6c6a57d5..f9a771d1566ad469d09e3159b588245602563e1b 100644 (file)
@@ -64,8 +64,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -259,7 +259,7 @@ class GameBoard(QWidget):
         quit = QPushButton("&Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange("ANGLE")
         angle.set_range(5, 70)
index e4f1c350d5de2d9735dc4f415e04916e6c906f13..3c94408f30a6cda9be6a1646a6fab349c68433be 100644 (file)
@@ -65,8 +65,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -174,7 +174,8 @@ class CannonField(QWidget):
             self._auto_shoot_timer.stop()
             self.hit.emit()
             self.can_shoot.emit(True)
-        elif shot_r.x() > self.width() or shot_r.y() > self.height() or shot_r.intersects(self.barrier_rect()):
+        elif (shot_r.x() > self.width() or shot_r.y() > self.height()
+                or shot_r.intersects(self.barrier_rect())):
             self._auto_shoot_timer.stop()
             self.missed.emit()
             self.can_shoot.emit(True)
@@ -301,7 +302,7 @@ class GameBoard(QWidget):
         quit = QPushButton("&Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange("ANGLE")
         angle.set_range(5, 70)
index ba0ebc41b397c3e34a6a4218678324e33def6b39..37a2dc9dd313bb319dedfc5b7a7fdcd531857918 100644 (file)
@@ -20,7 +20,7 @@ class MyWidget(QWidget):
         self.quit.setGeometry(62, 40, 75, 30)
         self.quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        self.quit.clicked.connect(qApp.quit)
+        self.quit.clicked.connect(qApp.quit)  # noqa: F821
 
 
 if __name__ == '__main__':
index 42faeed011a18ad0023b4433f0529aca6ab56cb7..ed5d085f86741d79d0d124826b86ce33ffe4f26f 100644 (file)
@@ -25,7 +25,7 @@ class MyWidget(QWidget):
         slider.setRange(0, 99)
         slider.setValue(0)
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
         slider.valueChanged.connect(lcd.display)
 
         layout = QVBoxLayout(self)
index 1cc2906f2c31f0b4025d7c3a9b94b14130d2833c..ea2e044e6ca186a2e02813ab9c26b2f9b55ba7e6 100644 (file)
@@ -33,7 +33,7 @@ class MyWidget(QWidget):
 
         quit = QPushButton("Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         layout = QVBoxLayout(self)
         layout.addWidget(quit)
index 51128e6c7cfef2c5bffed601161b59282f4ae54b..1175107b87389bc3a21980330a59969c0979ca8c 100644 (file)
@@ -46,7 +46,7 @@ class MyWidget(QWidget):
 
         quit = QPushButton("Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         previous_range = None
 
index b82e24a012cd4a5a20c6f97aff8262a237e0ff32..9bb5516b8e26ed21f647cf7ddf4da2df44e6dcce 100644 (file)
@@ -43,8 +43,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning("LCDRange.setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -88,7 +88,7 @@ class MyWidget(QWidget):
         quit = QPushButton("Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange()
         angle.set_range(5, 70)
index 297e98e509b6bc7cf7c5c8ed07add579236f14c8..7cdda4e7b6671696f03f09091c045151c68b5a51 100644 (file)
@@ -43,8 +43,8 @@ class LCDRange(QWidget):
     def set_range(self, minValue, maxValue):
         if minValue < 0 or maxValue > 99 or minValue > maxValue:
             qWarning(f"LCDRange::setRange({minValue}, {maxValue})\n"
-                    "\tRange must be 0..99\n"
-                    "\tand minValue must not be greater than maxValue")
+                     "\tRange must be 0..99\n"
+                     "\tand minValue must not be greater than maxValue")
             return
 
         self.slider.setRange(minValue, maxValue)
@@ -94,7 +94,7 @@ class MyWidget(QWidget):
         quit = QPushButton("Quit")
         quit.setFont(QFont("Times", 18, QFont.Bold))
 
-        quit.clicked.connect(qApp.quit)
+        quit.clicked.connect(qApp.quit)  # noqa: F821
 
         angle = LCDRange()
         angle.set_range(5, 70)
index 73c993e5f45137dba9a5e3232d198dce8461587a..f39ec462cbabd7c2f596b9815f4f8ccf07b8ff9c 100644 (file)
@@ -32,7 +32,7 @@ class MyModel(QAbstractTableModel):
                 return "<--left"
             if row == 1 and col == 1:
                 return "right-->"
-            return f"Row{row}, Column{col+1}"
+            return f"Row{row}, Column{col + 1}"
 
         elif role == Qt.FontRole:
             if row == 0 and col == 0:  # change font only for cell(0,0)
index e277dd1e83ca731108efe97d041f0d1a9461c097..2148ec5d36b09b320d4fb26744d888fc64e963aa 100644 (file)
@@ -43,6 +43,7 @@ class MyModel(QAbstractTableModel):
         self.dataChanged.emit(top_left, top_left, [Qt.DisplayRole])
 #! [3]
 
+
 if __name__ == '__main__':
     app = QApplication(sys.argv)
     table_view = QTableView()
index 09300560cc6b6ef5af46f17e13681135c4b067d1..cac3c6d5372e880c8ae2d4d07d3fbe53b79e62db 100644 (file)
@@ -8,6 +8,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow, QTreeView
 
 """PySide6 port of the widgets/tutorials/modelview/6_treeview example from Qt v6.x"""
 
+
 #! [1]
 class MainWindow(QMainWindow):
     def __init__(self, parent=None):
index 6c519c8654defe4c59155e5f11e82a28c209881e..c879d8f67769cb67df1891dd1e42b15026ce467f 100644 (file)
@@ -9,6 +9,7 @@ from PySide6.QtWidgets import QApplication, QMainWindow, QTreeView
 
 """PySide6 port of the widgets/tutorials/modelview/7_selections example from Qt v6.x"""
 
+
 #! [1]
 class MainWindow(QMainWindow):
     def __init__(self, parent=None):
index 1df2a3b74613e1a9aa03c5b1e3262df37147b059..0f01f96842e873a13b112114ad843bb91fdbe565 100644 (file)
@@ -127,7 +127,7 @@ class CharacterWidget(QWidget):
                                      self._square_size, self._square_size, QBrush(Qt.red))
 
                 text = chr(key)
-                painter.drawText(column * self._square_size + (self._square_size / 2) -
-                                 font_metrics.horizontalAdvance(text) / 2,
+                painter.drawText(column * self._square_size + (self._square_size / 2)
+                                 font_metrics.horizontalAdvance(text) / 2,
                                  row * self._square_size + 4 + font_metrics.ascent(),
                                  text)
index 5f0e2bce45cbc095adee9186e6e1d15b200a2a3d..d79285def79494c0a940b8292d7f8a454193c582 100644 (file)
@@ -30,7 +30,7 @@ class MainWindow(QMainWindow):
         file_menu.addAction("Quit", self.close)
         help_menu = self.menuBar().addMenu("Help")
         help_menu.addAction("Show Font Info", self.show_info)
-        help_menu.addAction("About &Qt", qApp.aboutQt)
+        help_menu.addAction("About &Qt", qApp.aboutQt)  # noqa: F821
 
         central_widget = QWidget()
 
index ef800d9c02737f46a4ddf1bb875e581b7800e5ed..d13275d24066c8c780a9ecc86fce0ac1d04b8587 100644 (file)
@@ -1,6 +1,8 @@
 Digital Clock Example
 =====================
 
+.. tags:: Android
+
 The Digital Clock example shows how to use QLCDNumber to display a number with
 LCD-like digits.
 
index 36cbb34487ccc7f92a7b7d88d0133e0f8098b859..b5df2aa3532b8c77298bdc36b22a9ff920483d05 100644 (file)
@@ -52,7 +52,7 @@ class TetrixWindow(QWidget):
 
         start_button.clicked.connect(self.board.start)
         pause_button.clicked.connect(self.board.pause)
-        quit_button.clicked.connect(qApp.quit)
+        quit_button.clicked.connect(qApp.quit)  # noqa: F821
         self.board.score_changed.connect(score_lcd.display)
         self.board.level_changed.connect(level_lcd.display)
         self.board.lines_removed_changed.connect(lines_lcd.display)
@@ -134,11 +134,11 @@ class TetrixBoard(QFrame):
 
     def sizeHint(self):
         return QSize(TetrixBoard.board_width * 15 + self.frameWidth() * 2,
-                TetrixBoard.board_height * 15 + self.frameWidth() * 2)
+                     TetrixBoard.board_height * 15 + self.frameWidth() * 2)
 
     def minimum_size_hint(self):
         return QSize(TetrixBoard.board_width * 5 + self.frameWidth() * 2,
-                TetrixBoard.board_height * 5 + self.frameWidth() * 2)
+                     TetrixBoard.board_height * 5 + self.frameWidth() * 2)
 
     @Slot()
     def start(self):
@@ -190,16 +190,17 @@ class TetrixBoard(QFrame):
                     shape = self.shape_at(j, TetrixBoard.board_height - i - 1)
                     if shape != Piece.NoShape:
                         self.draw_square(painter,
-                                rect.left() + j * self.square_width(),
-                                board_top + i * self.square_height(), shape)
+                                         rect.left() + j * self.square_width(),
+                                         board_top + i * self.square_height(), shape)
 
             if self._cur_piece.shape() != Piece.NoShape:
                 for i in range(4):
                     x = self._cur_x + self._cur_piece.x(i)
                     y = self._cur_y - self._cur_piece.y(i)
                     self.draw_square(painter, rect.left() + x * self.square_width(),
-                            board_top + (TetrixBoard.board_height - y - 1) * self.square_height(),
-                            self._cur_piece.shape())
+                                     board_top
+                                     + (TetrixBoard.board_height - y - 1) * self.square_height(),
+                                     self._cur_piece.shape())
 
     def keyPressEvent(self, event):
         if not self._is_started or self._is_paused or self._cur_piece.shape() == Piece.NoShape:
@@ -234,7 +235,8 @@ class TetrixBoard(QFrame):
             super(TetrixBoard, self).timerEvent(event)
 
     def clear_board(self):
-        self.board = [Piece.NoShape for i in range(TetrixBoard.board_height * TetrixBoard.board_width)]
+        self.board = [
+            Piece.NoShape for _ in range(TetrixBoard.board_height * TetrixBoard.board_width)]
 
     def drop_down(self):
         drop_height = 0
@@ -328,7 +330,7 @@ class TetrixBoard(QFrame):
             x = self._next_piece.x(i) - self._next_piece.min_x()
             y = self._next_piece.y(i) - self._next_piece.min_y()
             self.draw_square(painter, x * self.square_width(),
-                    y * self.square_height(), self._next_piece.shape())
+                             y * self.square_height(), self._next_piece.shape())
 
         self.nextPieceLabel.setPixmap(pixmap)
 
@@ -349,11 +351,10 @@ class TetrixBoard(QFrame):
 
     def draw_square(self, painter, x, y, shape):
         color_table = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC,
-                      0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
+                       0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00]
 
         color = QColor(color_table[shape])
-        painter.fillRect(x + 1, y + 1, self.square_width() - 2,
-                self.square_height() - 2, color)
+        painter.fillRect(x + 1, y + 1, self.square_width() - 2, self.square_height() - 2, color)
 
         painter.setPen(color.lighter())
         painter.drawLine(x, y + self.square_height() - 1, x, y)
@@ -361,25 +362,25 @@ class TetrixBoard(QFrame):
 
         painter.setPen(color.darker())
         painter.drawLine(x + 1, y + self.square_height() - 1,
-                x + self.square_width() - 1, y + self.square_height() - 1)
+                         x + self.square_width() - 1, y + self.square_height() - 1)
         painter.drawLine(x + self.square_width() - 1,
-                y + self.square_height() - 1, x + self.square_width() - 1, y + 1)
+                         y + self.square_height() - 1, x + self.square_width() - 1, y + 1)
 
 
 class TetrixPiece(object):
     coords_table = (
-        ((0, 0),     (0, 0),     (0, 0),     (0, 0)),
-        ((0, -1),    (0, 0),     (-1, 0),    (-1, 1)),
-        ((0, -1),    (0, 0),     (1, 0),     (1, 1)),
-        ((0, -1),    (0, 0),     (0, 1),     (0, 2)),
-        ((-1, 0),    (0, 0),     (1, 0),     (0, 1)),
-        ((0, 0),     (1, 0),     (0, 1),     (1, 1)),
-        ((-1, -1),   (0, -1),    (0, 0),     (0, 1)),
-        ((1, -1),    (0, -1),    (0, 0),     (0, 1))
+        ((0, 0), (0, 0), (0, 0), (0, 0)),
+        ((0, -1), (0, 0), (-1, 0), (-1, 1)),
+        ((0, -1), (0, 0), (1, 0), (1, 1)),
+        ((0, -1), (0, 0), (0, 1), (0, 2)),
+        ((-1, 0), (0, 0), (1, 0), (0, 1)),
+        ((0, 0), (1, 0), (0, 1), (1, 1)),
+        ((-1, -1), (0, -1), (0, 0), (0, 1)),
+        ((1, -1), (0, -1), (0, 0), (0, 1))
     )
 
     def __init__(self):
-        self.coords = [[0,0] for _ in range(4)]
+        self.coords = [[0, 0] for _ in range(4)]
         self._piece_shape = Piece.NoShape
 
         self.set_shape(Piece.NoShape)
index bf1f523e2bcf07a397de7b7b01e1407263faf7b5..d43ab26a59017c358bccde107812d9d59233a729 100644 (file)
@@ -164,8 +164,8 @@ class WidgetGallery(QDialog):
         top_layout.addStretch(1)
         top_layout.addWidget(disable_widgets_checkbox)
 
-        dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.Help |
-                                            QDialogButtonBox.Close)
+        dialog_buttonbox = QDialogButtonBox(QDialogButtonBox.Help
+                                            QDialogButtonBox.Close)
         init_widget(dialog_buttonbox, "dialogButtonBox")
         dialog_buttonbox.helpRequested.connect(launch_module_help)
         dialog_buttonbox.rejected.connect(self.reject)
index 4fbc843e1c121dd88c081495cf61b97b695c9a5f..a35aeb0f2ff7a1c48cceffebd3abaa4e0174a678 100644 (file)
@@ -7,7 +7,7 @@
 import sys
 
 from PySide6.QtCore import QDir, QFile, Qt, QTextStream
-from PySide6.QtGui import QAction, QIcon
+from PySide6.QtGui import QAction, QIcon, QKeySequence
 from PySide6.QtWidgets import (QApplication, QFileDialog, QHeaderView,
                                QMainWindow, QMessageBox, QStyle, QTreeWidget,
                                QTreeWidgetItem)
@@ -30,8 +30,8 @@ class MainWindow(QMainWindow):
 
     def open(self):
         file_name = QFileDialog.getOpenFileName(self,
-                "Open Bookmark File", QDir.currentPath(),
-                "XBEL Files (*.xbel *.xml)")[0]
+                                                "Open Bookmark File", QDir.currentPath(),
+                                                "XBEL Files (*.xbel *.xml)")[0]
 
         if not file_name:
             return
@@ -40,7 +40,7 @@ class MainWindow(QMainWindow):
         if not in_file.open(QFile.ReadOnly | QFile.Text):
             reason = in_file.errorString()
             QMessageBox.warning(self, "DOM Bookmarks",
-                    f"Cannot read file {file_name}:\n{reason}.")
+                                f"Cannot read file {file_name}:\n{reason}.")
             return
 
         if self._xbel_tree.read(in_file):
@@ -48,8 +48,8 @@ class MainWindow(QMainWindow):
 
     def save_as(self):
         file_name = QFileDialog.getSaveFileName(self,
-                "Save Bookmark File", QDir.currentPath(),
-                "XBEL Files (*.xbel *.xml)")[0]
+                                                "Save Bookmark File", QDir.currentPath(),
+                                                "XBEL Files (*.xbel *.xml)")[0]
 
         if not file_name:
             return
@@ -58,7 +58,7 @@ class MainWindow(QMainWindow):
         if not out_file.open(QFile.WriteOnly | QFile.Text):
             reason = out_file.errorString()
             QMessageBox.warning(self, "DOM Bookmarks",
-                    "Cannot write file {fileName}:\n{reason}.")
+                                f"Cannot write file {file_name}:\n{reason}.")
             return
 
         if self._xbel_tree.write(out_file):
@@ -66,25 +66,26 @@ class MainWindow(QMainWindow):
 
     def about(self):
         QMessageBox.about(self, "About DOM Bookmarks",
-            "The <b>DOM Bookmarks</b> example demonstrates how to use Qt's "
-            "DOM classes to read and write XML documents.")
+                          "The <b>DOM Bookmarks</b> example demonstrates how to use Qt's "
+                          "DOM classes to read and write XML documents.")
 
     def create_menus(self):
         self._file_menu = self.menuBar().addMenu("&File")
         self._file_menu.addAction(QAction("&Open...", self,
-                shortcut=QKeySequence(Qt.CTRL | Qt.Key_O), triggered=self.open))
+                                          shortcut=QKeySequence(
+                                              Qt.CTRL | Qt.Key_O), triggered=self.open))
         self._file_menu.addAction(QAction("&Save As...", self,
-                shortcut=QKeySequence(Qt.CTRL | Qt.Key_S), triggered=self.save_as))
+                                          shortcut=QKeySequence(
+                                              Qt.CTRL | Qt.Key_S), triggered=self.save_as))
         self._file_menu.addAction(QAction("E&xit", self,
-                shortcut=QKeySequence(Qt.CTRL | Qt.Key_Q), triggered=self.close))
+                                          shortcut=QKeySequence(
+                                              Qt.CTRL | Qt.Key_Q), triggered=self.close))
 
         self.menuBar().addSeparator()
 
         self._help_menu = self.menuBar().addMenu("&Help")
-        self._help_menu.addAction(QAction("&About", self,
-                triggered=self.about))
-        self._help_menu.addAction(QAction("About &Qt", self,
-                triggered=qApp.aboutQt))
+        self._help_menu.addAction(QAction("&About", self, triggered=self.about))
+        self._help_menu.addAction(QAction("About &Qt", self, triggered=qApp.aboutQt))  # noqa: F821
 
 
 class XbelTree(QTreeWidget):
@@ -102,26 +103,27 @@ class XbelTree(QTreeWidget):
         self._bookmark_icon = QIcon()
 
         self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirClosedIcon),
-                QIcon.Normal, QIcon.Off)
+                                    QIcon.Normal, QIcon.Off)
         self._folder_icon.addPixmap(self.style().standardPixmap(QStyle.SP_DirOpenIcon),
-                QIcon.Normal, QIcon.On)
+                                    QIcon.Normal, QIcon.On)
         self._bookmark_icon.addPixmap(self.style().standardPixmap(QStyle.SP_FileIcon))
 
     def read(self, device):
         ok, errorStr, errorLine, errorColumn = self._dom_document.setContent(device, True)
         if not ok:
             QMessageBox.information(self.window(), "DOM Bookmarks",
-                    f"Parse error at line {errorLine}, column {errorColumn}:\n{errorStr}")
+                                    f"Parse error at line {errorLine}, "
+                                    f"column {errorColumn}:\n{errorStr}")
             return False
 
         root = self._dom_document.documentElement()
         if root.tagName() != 'xbel':
             QMessageBox.information(self.window(), "DOM Bookmarks",
-                    "The file is not an XBEL file.")
+                                    "The file is not an XBEL file.")
             return False
         elif root.hasAttribute('version') and root.attribute('version') != '1.0':
             QMessageBox.information(self.window(), "DOM Bookmarks",
-                    "The file is not an XBEL version 1.0 file.")
+                                    "The file is not an XBEL version 1.0 file.")
             return False
 
         self.clear()
index 174a8878774f5bfe5c75e7c8364b40f404070e5f..70eb85bf65f853ea10ec9c50714effd0b94d479b 100644 (file)
@@ -1,6 +1,11 @@
 sphinx==7.2.6
 sphinx-design==0.5.0
 sphinx-copybutton==0.5.2
+sphinx-tags==0.3.1
 myst-parser==2.0.0
-furo==2023.9.10
+# FIXME: Using fork in order to enable the 'collapse_navbar=True'
+# option for the sphinx-theme. Upstream proposal:
+# https://github.com/pradyunsg/furo/pull/748#issuecomment-1895448722
+# furo==2023.9.10
+furo @ git+https://github.com/cmaureir/furo@add_collapse
 graphviz==0.20
index 8a3d9912fa071e988688229a9b11ffa6ff287c4b..2b279db3b1e7e42c94e2520595259483c63039f2 100644 (file)
@@ -1,16 +1,16 @@
-setuptools==67.8.0
-packaging
-build==0.7
-six
-wheel>=0.35
+# Build dependencies
+setuptools==69.0.3
+packaging==23.2
+build==1.0.3
+wheel==0.42.0
+distro==1.9.0; sys_platform == 'linux'
+patchelf==0.17.2; sys_platform == 'linux'
+# 1.24.4 is the last version that supports Python 3.8
+numpy<1.25; python_version < '3.9'
+numpy==1.26.3; python_version >= '3.9'
+
+# For examples
 PyOpenGL
+
+# For tests
 pyinstaller==3.6; platform_machine != 'aarch64'
-numpy
-nuitka==1.4.8; platform_machine != 'aarch64'
-distro; sys_platform == 'linux'
-patchelf==0.15; sys_platform == 'linux'
-pkginfo; sys_platform == 'linux'
-jinja2;  sys_platform == 'linux'
-buildozer==1.5.0;  sys_platform == 'linux'
-tqdm; sys_platform == 'linux'
-gitpython; sys_platform == 'linux'
index 795ff669be425b7d74a4c0942cfe75eea20a07f3..d8a353300ec9b8e8dd7002e3867c840d8880e2e2 100644 (file)
@@ -25,7 +25,8 @@ else()
             ${CMAKE_CURRENT_SOURCE_DIR}/qml.py
             ${CMAKE_CURRENT_SOURCE_DIR}/qtpy2cpp.py
             ${CMAKE_CURRENT_SOURCE_DIR}/deploy.py
-            ${CMAKE_CURRENT_SOURCE_DIR}/android_deploy.py)
+            ${CMAKE_CURRENT_SOURCE_DIR}/android_deploy.py
+            ${CMAKE_CURRENT_SOURCE_DIR}/requirements-android.txt)
 
     set(directories ${CMAKE_CURRENT_SOURCE_DIR}/deploy_lib
                     ${CMAKE_CURRENT_SOURCE_DIR}/project)
index 8ac8aa510ae5873bbda33ba0117824c4250f4fa5..8ef59781fbd016008745df8ba8346f54ad77156b 100644 (file)
@@ -8,11 +8,11 @@ import traceback
 from pathlib import Path
 from textwrap import dedent
 
-from deploy_lib import (MAJOR_VERSION, cleanup, config_option_exists,
-                        find_pyside_modules, get_config,
-                        install_python_dependencies, setup_python)
-from deploy_lib.android import (AndroidData, Buildozer, extract_and_copy_jar,
-                                get_wheel_android_arch)
+from deploy_lib import (setup_python, create_config_file, cleanup, install_python_dependencies,
+                        config_option_exists, MAJOR_VERSION)
+from deploy_lib.android import AndroidData, AndroidConfig
+from deploy_lib.android.buildozer import Buildozer
+
 
 """ pyside6-android-deploy deployment tool
 
@@ -67,8 +67,8 @@ HELP_EXTRA_MODULES = dedent("""
                             """)
 
 
-def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = None, ndk_path: Path = None,
-         sdk_path: Path = None, config_file: Path = None, init: bool = False,
+def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = None,
+         ndk_path: Path = None, sdk_path: Path = None, config_file: Path = None, init: bool = False,
          loglevel=logging.WARNING, dry_run: bool = False, keep_deployment_files: bool = False,
          force: bool = False, extra_ignore_dirs: str = None, extra_modules_grouped: str = None):
 
@@ -87,7 +87,6 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
                 extra_modules.append(extra_module)
 
     main_file = Path.cwd() / "main.py"
-    generated_files_path = None
     if not main_file.exists():
         raise RuntimeError(("[DEPLOY] For Android deployment to work, the main"
                             " entrypoint Python file should be named 'main.py'"
@@ -98,16 +97,25 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
                                ndk_path=ndk_path, sdk_path=sdk_path)
 
     python = setup_python(dry_run=dry_run, force=force, init=init)
-    config = get_config(python_exe=python.exe, dry_run=dry_run, config_file=config_file,
-                        main_file=main_file, android_data=android_data, is_android=True)
+
+    config_file_exists = config_file and Path(config_file).exists()
+
+    if config_file_exists:
+        logging.info(f"[DEPLOY] Using existing config file {config_file}")
+    else:
+        config_file = create_config_file(dry_run=dry_run, config_file=config_file,
+                                         main_file=main_file)
+
+    config = AndroidConfig(config_file=config_file, source_file=main_file,
+                           python_exe=python.exe, dry_run=dry_run, android_data=android_data,
+                           existing_config_file=config_file_exists,
+                           extra_ignore_dirs=extra_ignore_dirs)
 
     if not config.wheel_pyside and not config.wheel_shiboken:
         raise RuntimeError(f"[DEPLOY] No PySide{MAJOR_VERSION} and Shiboken{MAJOR_VERSION} wheels"
                            "found")
 
-    source_file = config.project_dir / config.source_file
-    generated_files_path = source_file.parent / "deployment"
-    cleanup(generated_files_path=generated_files_path, config=config, is_android=True)
+    cleanup(config=config, is_android=True)
 
     install_python_dependencies(config=config, python=python, init=init,
                                 packages="android_packages", is_android=True)
@@ -117,29 +125,11 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
         config.title = name
 
     try:
-        # check which modules are needed
-        if not config.modules:
-            config.modules = find_pyside_modules(project_dir=config.project_dir,
-                                                 extra_ignore_dirs=extra_ignore_dirs,
-                                                 project_data=config.project_data)
-            logging.info("The following PySide modules were found from the python files of "
-                         f"the project {config.modules}")
-        config.modules.extend(extra_modules)
-
-        # extract out and copy .jar files to {generated_files_path}
-        if not config.jars_dir or not Path(config.jars_dir).exists() and not dry_run:
-            logging.info("[DEPLOY] Extract and copy jar files from PySide6 wheel to "
-                         f"{generated_files_path}")
-            config.jars_dir = extract_and_copy_jar(wheel_path=config.wheel_pyside,
-                                                   generated_files_path=generated_files_path)
-
-        # find architecture from wheel name
-        if not config.arch:
-            arch = get_wheel_android_arch(wheel=config.wheel_pyside)
-            if not arch:
-                raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
-                                   "platform name")
-            config.arch = arch
+        config.modules += extra_modules
+
+        # this cannot be done when config file is initialized because cleanup() removes it
+        # so this can only be done after the cleanup()
+        config.find_and_set_jars_dir()
 
         # TODO: include qml files from pysidedeploy.spec rather than from extensions
         # buildozer currently includes all the files with .qml extension
@@ -147,7 +137,7 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
         # init buildozer
         Buildozer.dry_run = dry_run
         logging.info("[DEPLOY] Creating buildozer.spec file")
-        Buildozer.initialize(pysidedeploy_config=config, generated_files_path=generated_files_path)
+        Buildozer.initialize(pysidedeploy_config=config)
 
         # writing config file
         if not dry_run:
@@ -167,17 +157,16 @@ def main(name: str = None, pyside_wheel: Path = None, shiboken_wheel: Path = Non
             buildozer_build_dir = config.project_dir / ".buildozer"
             if not buildozer_build_dir.exists():
                 logging.info(f"[DEPLOY] Unable to copy {buildozer_build_dir} to "
-                             f"{generated_files_path}. {buildozer_build_dir} does not exist")
-            logging.info(f"[DEPLOY] Copying {str(buildozer_build_dir)} to "
-                         f"{str(generated_files_path)}")
-            shutil.move(buildozer_build_dir, generated_files_path)
+                             f"{config.generated_files_path}. {buildozer_build_dir} does not exist")
+            logging.info(f"[DEPLOY] copy {buildozer_build_dir} to {config.generated_files_path}")
+            shutil.move(buildozer_build_dir, config.generated_files_path)
 
         logging.info(f"[DEPLOY] apk created in {config.exe_dir}")
     except Exception:
         print(f"Exception occurred: {traceback.format_exc()}")
     finally:
-        if generated_files_path and config and not keep_deployment_files:
-            cleanup(generated_files_path=generated_files_path, config=config, is_android=True)
+        if config.generated_files_path and config and not keep_deployment_files:
+            cleanup(config=config, is_android=True)
 
     logging.info("[DEPLOY] End")
 
@@ -193,6 +182,7 @@ if __name__ == "__main__":
     )
 
     parser.add_argument("-c", "--config-file", type=lambda p: Path(p).absolute(),
+                        default=(Path.cwd() / "pysidedeploy.spec"),
                         help="Path to the .spec config file")
 
     parser.add_argument(
index b86824a6092607016f89bf027e026df9c23464f0..0aea807a8472ae2bafabe78929c8ba5155654646 100644 (file)
@@ -35,7 +35,7 @@ from pathlib import Path
 from textwrap import dedent
 
 from deploy_lib import (MAJOR_VERSION, Config, cleanup, config_option_exists,
-                        finalize, get_config, install_python_dependencies,
+                        finalize, create_config_file, install_python_dependencies,
                         setup_python)
 
 
@@ -54,29 +54,34 @@ def main(main_file: Path = None, name: str = None, config_file: Path = None, ini
 
     # Nuitka command to run
     command_str = None
-    generated_files_path = None
     config = None
     logging.info("[DEPLOY] Start")
 
     python = setup_python(dry_run=dry_run, force=force, init=init)
-    config = get_config(python_exe=python.exe, dry_run=dry_run, config_file=config_file,
-                        main_file=main_file)
+    config_file_exists = config_file and Path(config_file).exists()
+
+    if config_file_exists:
+        logging.info(f"[DEPLOY] Using existing config file {config_file}")
+    else:
+        config_file = create_config_file(dry_run=dry_run, config_file=config_file,
+                                         main_file=main_file)
+
+    config = Config(config_file=config_file, source_file=main_file, python_exe=python.exe,
+                    dry_run=dry_run, existing_config_file=config_file_exists)
 
     # set application name
     if name:
         config.title = name
 
-    source_file = config.project_dir / config.source_file
-
-    generated_files_path = source_file.parent / "deployment"
-    cleanup(generated_files_path=generated_files_path, config=config)
+    cleanup(config=config)
 
     install_python_dependencies(config=config, python=python, init=init,
                                 packages="packages")
 
     # required by Nuitka for pyenv Python
-    if python.is_pyenv_python():
-        config.extra_args += " --static-libpython=no"
+    add_arg = " --static-libpython=no"
+    if python.is_pyenv_python() and add_arg not in config.extra_args:
+        config.extra_args += add_arg
 
     # writing config file
     # in the case of --dry-run, we use default.spec as reference. Do not save the changes
@@ -94,18 +99,16 @@ def main(main_file: Path = None, name: str = None, config_file: Path = None, ini
         if not dry_run:
             logging.info("[DEPLOY] Deploying application")
 
-        command_str = python.create_executable(
-                        source_file=source_file,
-                        extra_args=config.extra_args,
-                        config=config,
-                    )
+        command_str = python.create_executable(source_file=config.source_file,
+                                               extra_args=config.extra_args,
+                                               config=config)
     except Exception:
         print(f"[DEPLOY] Exception occurred: {traceback.format_exc()}")
     finally:
-        if generated_files_path and config:
-            finalize(generated_files_path=generated_files_path, config=config)
+        if config.generated_files_path and config:
+            finalize(config=config)
             if not keep_deployment_files:
-                cleanup(generated_files_path=generated_files_path, config=Config)
+                cleanup(config=config)
 
     logging.info("[DEPLOY] End")
     return command_str
index c0085fbe1cf4295e3f1435aa56e3308de83d51c3..eef1668c5edf0279fbc5b56673afd1e2a8de43e5 100644 (file)
@@ -1,6 +1,7 @@
 {
     "files": ["deploy.py", "deploy_lib/__init__.py", "deploy_lib/commands.py", "deploy_lib/config.py",
-              "deploy_lib/default.spec", "deploy_lib/nuitka_helper.py", "deploy_lib/pyside_icon.jpg",
+              "deploy_lib/default.spec", "deploy_lib/nuitka_helper.py", "deploy_lib/pyside_icon.ico",
+              "deploy_lib/pyside_icon.icns","deploy_lib/pyside_icon.jpg",
               "deploy_lib/python_helper.py", "deploy_lib/deploy_util.py"
               ]
 }
index 468a03673c85d05365d6ea7f14720ba133232918..8c5d8b4ef48280d02fc05a301bdac043872d6f87 100644 (file)
@@ -1,13 +1,25 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 import sys
+from pathlib import Path
 
 MAJOR_VERSION = 6
-EXE_FORMAT = ".exe" if sys.platform == "win32" else ".bin"
+
+if sys.platform == "win32":
+    IMAGE_FORMAT = ".ico"
+    EXE_FORMAT = ".exe"
+elif sys.platform == "darwin":
+    IMAGE_FORMAT = ".icns"
+    EXE_FORMAT = ".bin"
+else:
+    IMAGE_FORMAT = ".jpg"
+    EXE_FORMAT = ".bin"
+
+DEFAULT_APP_ICON = str((Path(__file__).parent / f"pyside_icon{IMAGE_FORMAT}").resolve())
 
 from .commands import run_command
-from .config import BaseConfig, Config
 from .nuitka_helper import Nuitka
-from .deploy_util import (cleanup, config_option_exists, finalize, get_config,
-                          install_python_dependencies, setup_python)
 from .python_helper import PythonExecutable, find_pyside_modules
+from .config import BaseConfig, Config
+from .deploy_util import (cleanup, finalize, create_config_file, setup_python,
+                          install_python_dependencies, config_option_exists)
index 907e12e1fb88f0eebee61a97aa92090425b5b41c..59cd510c58ccbd4508198867d774402f5e253b93 100644 (file)
@@ -1,7 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
-from .android_helper import (AndroidData, create_recipe, extract_and_copy_jar,
-                             find_lib_dependencies, find_qtlibs_in_wheel,
-                             get_llvm_readobj, get_wheel_android_arch)
-from .buildozer import Buildozer
+from .android_helper import (create_recipe, extract_and_copy_jar,
+                             get_wheel_android_arch, AndroidData, get_llvm_readobj,
+                             find_lib_dependencies, find_qtlibs_in_wheel)
+from .android_config import AndroidConfig
diff --git a/sources/pyside-tools/deploy_lib/android/android_config.py b/sources/pyside-tools/deploy_lib/android/android_config.py
new file mode 100644 (file)
index 0000000..442672a
--- /dev/null
@@ -0,0 +1,256 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+import logging
+
+from typing import List
+from pathlib import Path
+
+from . import extract_and_copy_jar, get_wheel_android_arch
+from .. import Config, find_pyside_modules
+
+ANDROID_NDK_VERSION = "25c"
+ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
+
+
+class AndroidConfig(Config):
+    """
+    Wrapper class around pysidedeploy.spec file for pyside6-android-deploy
+    """
+    def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
+                 android_data, existing_config_file: bool = False,
+                 extra_ignore_dirs: List[str] = None):
+        super().__init__(config_file=config_file, source_file=source_file, python_exe=python_exe,
+                         dry_run=dry_run, existing_config_file=existing_config_file)
+
+        self.extra_ignore_dirs = extra_ignore_dirs
+
+        if android_data.wheel_pyside:
+            self.wheel_pyside = android_data.wheel_pyside
+        else:
+            wheel_pyside_temp = self.get_value("android", "wheel_pyside")
+            if not wheel_pyside_temp:
+                raise RuntimeError("[DEPLOY] Unable to find PySide6 Android wheel")
+            self.wheel_pyside = Path(wheel_pyside_temp).resolve()
+
+        if android_data.wheel_shiboken:
+            self.wheel_shiboken = android_data.wheel_shiboken
+        else:
+            wheel_shiboken_temp = self.get_value("android", "wheel_shiboken")
+            if not wheel_shiboken_temp:
+                raise RuntimeError("[DEPLOY] Unable to find shiboken6 Android wheel")
+            self.wheel_shiboken = Path(wheel_shiboken_temp).resolve()
+
+        self.ndk_path = None
+        if android_data.ndk_path:
+            # from cli
+            self.ndk_path = android_data.ndk_path
+        else:
+            # from config
+            ndk_path_temp = self.get_value("buildozer", "ndk_path")
+            if ndk_path_temp:
+                self.ndk_path = Path(ndk_path_temp)
+            else:
+                ndk_path_temp = (ANDROID_DEPLOY_CACHE / "android-ndk"
+                                 / f"android-ndk-r{ANDROID_NDK_VERSION}")
+                if ndk_path_temp.exists():
+                    self.ndk_path = ndk_path_temp
+
+        if self.ndk_path:
+            print(f"Using Android NDK: {str(self.ndk_path)}")
+        else:
+            raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK "
+                                    "path either from the CLI or from pysidedeploy.spec")
+
+        self.sdk_path = None
+        if android_data.sdk_path:
+            self.sdk_path = android_data.sdk_path
+        else:
+            sdk_path_temp = self.get_value("buildozer", "sdk_path")
+            if sdk_path_temp:
+                self.sdk_path = Path(sdk_path_temp)
+            else:
+                sdk_path_temp = ANDROID_DEPLOY_CACHE / "android-sdk"
+                if sdk_path_temp.exists():
+                    self.sdk_path = sdk_path_temp
+                else:
+                    logging.info("[DEPLOY] Use default SDK from buildozer")
+
+        if self.sdk_path:
+            print(f"Using Android SDK: {str(self.sdk_path)}")
+
+        recipe_dir_temp = self.get_value("buildozer", "recipe_dir")
+        self.recipe_dir = Path(recipe_dir_temp) if recipe_dir_temp else None
+
+        self._jars_dir = []
+        jars_dir_temp = self.get_value("buildozer", "jars_dir")
+        if jars_dir_temp and Path(jars_dir_temp).resolve().exists():
+            self.jars_dir = Path(jars_dir_temp).resolve()
+
+        self._modules = []
+        if self.get_value("buildozer", "modules"):
+            self.modules = self.get_value("buildozer", "modules").split(",")
+        else:
+            self._find_and_set_pysidemodules()
+            self._find_and_set_qtquick_modules()
+
+        self._arch = None
+        if self.get_value("buildozer", "arch"):
+            self.arch = self.get_value("buildozer", "arch")
+        else:
+            self._find_and_set_arch()
+
+        self._local_libs = []
+        if self.get_value("buildozer", "local_libs"):
+            self.local_libs = self.get_value("buildozer", "local_libs").split(",")
+
+        self._qt_plugins = []
+        if self.get_value("android", "plugins"):
+            self._qt_plugins = self.get_value("android", "plugins").split(",")
+
+        self._mode = self.get_value("buildozer", "mode")
+
+    @property
+    def qt_plugins(self):
+        return self._qt_plugins
+
+    @qt_plugins.setter
+    def qt_plugins(self, qt_plugins):
+        self._qt_plugins = qt_plugins
+        self.set_value("android", "plugins", ",".join(qt_plugins))
+
+    @property
+    def ndk_path(self):
+        return self._ndk_path
+
+    @ndk_path.setter
+    def ndk_path(self, ndk_path: Path):
+        self._ndk_path = ndk_path.resolve() if ndk_path else None
+        if self._ndk_path:
+            self.set_value("buildozer", "ndk_path", str(self._ndk_path))
+
+    @property
+    def sdk_path(self) -> Path:
+        return self._sdk_path
+
+    @sdk_path.setter
+    def sdk_path(self, sdk_path: Path):
+        self._sdk_path = sdk_path.resolve() if sdk_path else None
+        if self._sdk_path:
+            self.set_value("buildozer", "sdk_path", str(self._sdk_path))
+
+    @property
+    def arch(self):
+        return self._arch
+
+    @arch.setter
+    def arch(self, arch):
+        self._arch = arch
+        self.set_value("buildozer", "arch", arch)
+
+    @property
+    def mode(self):
+        return self._mode
+
+    @property
+    def modules(self):
+        return self._modules
+
+    @modules.setter
+    def modules(self, modules):
+        self._modules = modules
+        self.set_value("buildozer", "modules", ",".join(modules))
+
+    @property
+    def local_libs(self):
+        return self._local_libs
+
+    @local_libs.setter
+    def local_libs(self, local_libs):
+        self._local_libs = local_libs
+        self.set_value("buildozer", "local_libs", ",".join(local_libs))
+
+    @property
+    def recipe_dir(self):
+        return self._recipe_dir
+
+    @recipe_dir.setter
+    def recipe_dir(self, recipe_dir: Path):
+        self._recipe_dir = recipe_dir.resolve() if recipe_dir else None
+        if self._recipe_dir:
+            self.set_value("buildozer", "recipe_dir", str(self._recipe_dir))
+
+    def recipes_exist(self):
+        if not self._recipe_dir:
+            return False
+
+        pyside_recipe_dir = Path(self.recipe_dir) / "PySide6"
+        shiboken_recipe_dir = Path(self.recipe_dir) / "shiboken6"
+
+        return pyside_recipe_dir.is_dir() and shiboken_recipe_dir.is_dir()
+
+    @property
+    def jars_dir(self) -> Path:
+        return self._jars_dir
+
+    @jars_dir.setter
+    def jars_dir(self, jars_dir: Path):
+        self._jars_dir = jars_dir.resolve() if jars_dir else None
+        if self._jars_dir:
+            self.set_value("buildozer", "jars_dir", str(self._jars_dir))
+
+    @property
+    def wheel_pyside(self) -> Path:
+        return self._wheel_pyside
+
+    @wheel_pyside.setter
+    def wheel_pyside(self, wheel_pyside: Path):
+        self._wheel_pyside = wheel_pyside.resolve() if wheel_pyside else None
+        if self._wheel_pyside:
+            self.set_value("android", "wheel_pyside", str(self._wheel_pyside))
+
+    @property
+    def wheel_shiboken(self) -> Path:
+        return self._wheel_shiboken
+
+    @wheel_shiboken.setter
+    def wheel_shiboken(self, wheel_shiboken: Path):
+        self._wheel_shiboken = wheel_shiboken.resolve() if wheel_shiboken else None
+        if self._wheel_shiboken:
+            self.set_value("android", "wheel_shiboken", str(self._wheel_shiboken))
+
+    def _find_and_set_pysidemodules(self):
+        self.modules = find_pyside_modules(project_dir=self.project_dir,
+                                           extra_ignore_dirs=self.extra_ignore_dirs,
+                                           project_data=self.project_data)
+        logging.info("The following PySide modules were found from the python files of "
+                     f"the project {self.modules}")
+
+    def find_and_set_jars_dir(self):
+        """Extract out and copy .jar files to {generated_files_path}
+        """
+        if not self.dry_run:
+            logging.info("[DEPLOY] Extract and copy jar files from PySide6 wheel to "
+                         f"{self.generated_files_path}")
+            self.jars_dir = extract_and_copy_jar(wheel_path=self.wheel_pyside,
+                                                 generated_files_path=self.generated_files_path)
+
+    def _find_and_set_arch(self):
+        """Find architecture from wheel name
+        """
+        self.arch = get_wheel_android_arch(wheel=self.wheel_pyside)
+        if not self.arch:
+            raise RuntimeError("[DEPLOY] PySide wheel corrupted. Wheel name should end with"
+                               "platform name")
+
+    def _find_and_set_qtquick_modules(self):
+        """Identify if QtQuick is used in QML files and add them as dependency
+        """
+        extra_modules = []
+
+        if "QtQuick" in self.qml_modules:
+            extra_modules.append("Quick")
+
+        if "QtQuick.Controls" in self.qml_modules:
+            extra_modules.append("QuickControls2")
+
+        self.modules += extra_modules
index 2299eab254566c49176d7e6a98740b047f124e59..2303436476d74037f0ea9481179413436cc6022a 100644 (file)
@@ -39,7 +39,7 @@ def create_recipe(version: str, component: str, wheel_path: str, generated_files
 
     qt_local_libs = []
     if local_libs:
-        qt_local_libs = [local_lib for local_lib in local_libs if local_lib.startswith("Qt6") ]
+        qt_local_libs = [local_lib for local_lib in local_libs if local_lib.startswith("Qt6")]
 
     rcp_tmpl_path = Path(__file__).parent / "recipes" / f"{component}"
     environment = Environment(loader=FileSystemLoader(rcp_tmpl_path))
index f66a4b01cc3c9ca23ec7176b10847876fcc53b38..3c188b1c01ee3eda25606ad62e1c5dcfcb6e13f4 100644 (file)
@@ -1,6 +1,7 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
+import sys
 import logging
 import re
 import tempfile
@@ -22,8 +23,7 @@ ALL_PYSIDE_MODULES = [module[2:] for module in PySide6.__all__]
 
 
 class BuildozerConfig(BaseConfig):
-    def __init__(self, buildozer_spec_file: Path, pysidedeploy_config: Config,
-                 generated_files_path: Path):
+    def __init__(self, buildozer_spec_file: Path, pysidedeploy_config: Config):
         super().__init__(buildozer_spec_file, comment_prefixes="#")
         self.set_value("app", "title", pysidedeploy_config.title)
         self.set_value("app", "package.name", pysidedeploy_config.title)
@@ -51,10 +51,6 @@ class BuildozerConfig(BaseConfig):
         self.set_value("app", "android.archs", self.arch)
 
         # p4a changes
-        logging.info("[DEPLOY] Using custom fork of python-for-android: "
-                     "https://github.com/shyamnathp/python-for-android/tree/pyside_support")
-        self.set_value("app", "p4a.fork", "shyamnathp")
-        self.set_value("app", "p4a.branch", "pyside_support_2")
         self.set_value("app", "p4a.bootstrap", "qt")
 
         self.qt_libs_path: zipfile.Path = (
@@ -64,11 +60,6 @@ class BuildozerConfig(BaseConfig):
         extra_modules = self.__find_dependent_qt_modules(pysidedeploy_config)
         logging.info(f"[DEPLOY] Other dependent modules to be added: {extra_modules}")
         pysidedeploy_config.modules = pysidedeploy_config.modules + extra_modules
-
-        # update the config file with the extra modules
-        if extra_modules:
-            pysidedeploy_config.update_config()
-
         modules = ",".join(pysidedeploy_config.modules)
 
         # gets the xml dependency files from Qt installation path
@@ -83,9 +74,6 @@ class BuildozerConfig(BaseConfig):
         self.__find_plugin_dependencies(dependency_files, dependent_plugins)
         pysidedeploy_config.qt_plugins += dependent_plugins
 
-        if local_libs or dependent_plugins:
-            pysidedeploy_config.update_config()
-
         local_libs = ",".join(pysidedeploy_config.local_libs)
 
         # create recipes
@@ -97,14 +85,15 @@ class BuildozerConfig(BaseConfig):
             version = Wheel(pysidedeploy_config.wheel_pyside).version
             create_recipe(version=version, component=f"PySide{MAJOR_VERSION}",
                           wheel_path=pysidedeploy_config.wheel_pyside,
-                          generated_files_path=generated_files_path,
+                          generated_files_path=pysidedeploy_config.generated_files_path,
                           qt_modules=pysidedeploy_config.modules,
                           local_libs=pysidedeploy_config.local_libs,
                           plugins=pysidedeploy_config.qt_plugins)
             create_recipe(version=version, component=f"shiboken{MAJOR_VERSION}",
                           wheel_path=pysidedeploy_config.wheel_shiboken,
-                          generated_files_path=generated_files_path)
-            pysidedeploy_config.recipe_dir = (generated_files_path / "recipes").resolve()
+                          generated_files_path=pysidedeploy_config.generated_files_path)
+            pysidedeploy_config.recipe_dir = ((pysidedeploy_config.generated_files_path
+                                              / "recipes").resolve())
         self.set_value('app', "p4a.local_recipes", str(pysidedeploy_config.recipe_dir))
 
         # add permissions
@@ -129,6 +118,9 @@ class BuildozerConfig(BaseConfig):
         # change final apk/aab path
         self.set_value("buildozer", "bin_dir", str(pysidedeploy_config.exe_dir.resolve()))
 
+        # set application icon
+        self.set_value("app", "icon.filename", pysidedeploy_config.icon)
+
         self.update_config()
 
     def __get_dependency_files(self, modules: List[str], arch: str) -> List[zipfile.Path]:
@@ -261,8 +253,8 @@ class BuildozerConfig(BaseConfig):
                 else:
                     continue
 
-                absolute_plugin_module_folder = (self.qt_libs_path.parent / "plugins" /
-                                                plugin_module_folder)
+                absolute_plugin_module_folder = (self.qt_libs_path.parent / "plugins"
+                                                 / plugin_module_folder)
 
                 if not absolute_plugin_module_folder.is_dir():
                     logging.warning(f"[DEPLOY] Qt plugin folder '{plugin_module_folder}' does not"
@@ -332,7 +324,7 @@ class Buildozer:
     dry_run = False
 
     @staticmethod
-    def initialize(pysidedeploy_config: Config, generated_files_path: Path):
+    def initialize(pysidedeploy_config: Config):
         project_dir = Path(pysidedeploy_config.project_dir)
         buildozer_spec = project_dir / "buildozer.spec"
         if buildozer_spec.exists():
@@ -341,14 +333,14 @@ class Buildozer:
             return
 
         # creates buildozer.spec config file
-        command = ["buildozer", "init"]
+        command = [sys.executable, "-m", "buildozer", "init"]
         run_command(command=command, dry_run=Buildozer.dry_run)
         if not Buildozer.dry_run:
             if not buildozer_spec.exists():
                 raise RuntimeError(f"buildozer.spec not found in {Path.cwd()}")
-            BuildozerConfig(buildozer_spec, pysidedeploy_config, generated_files_path)
+            BuildozerConfig(buildozer_spec, pysidedeploy_config)
 
     @staticmethod
     def create_executable(mode: str):
-        command = ["buildozer", "android", mode]
+        command = [sys.executable, "-m", "buildozer", "android", mode]
         run_command(command=command, dry_run=Buildozer.dry_run)
index 37669cff192a0f79d222173166dd869dd0f1e6b8..8a86157987b7a3e936cb2a03319cf7e0edfc0708 100644 (file)
@@ -35,30 +35,30 @@ class PySideRecipe(PythonRecipe):
         shutil.copyfile(lib_dir.parent.parent / "libpyside6.abi3.so",
                         Path(self.ctx.get_libs_dir(arch.arch)) / "libpyside6.abi3.so")
 
-        {% for module in qt_modules %}
+        {% for module in qt_modules %}  # noqa: E999
         shutil.copyfile(lib_dir.parent.parent / f"Qt{{ module }}.abi3.so",
                         Path(self.ctx.get_libs_dir(arch.arch)) / "Qt{{ module }}.abi3.so")
-        {% if module == "Qml" -%}
+        {% if module == "Qml" -%}  # noqa: E999
         shutil.copyfile(lib_dir.parent.parent / "libpyside6qml.abi3.so",
                         Path(self.ctx.get_libs_dir(arch.arch)) / "libpyside6qml.abi3.so")
-        {% endif %}
-        {% endfor %}
+        {% endif %}  # noqa: E999
+        {% endfor %}  # noqa: E999
 
-        {% for lib in qt_local_libs %}
+        {% for lib in qt_local_libs %}  # noqa: E999
         lib_path = lib_dir / f"lib{{ lib }}_{arch.arch}.so"
         if lib_path.exists():
             shutil.copyfile(lib_path,
                             Path(self.ctx.get_libs_dir(arch.arch)) / f"lib{{ lib }}_{arch.arch}.so")
-        {% endfor %}
+        {% endfor %}  # noqa: E999
 
-        {% for plugin_category,plugin_name in qt_plugins %}
+        {% for plugin_category,plugin_name in qt_plugins %}  # noqa: E999
         plugin_path = (lib_dir.parent / "plugins" / "{{ plugin_category }}" /
                       f"libplugins_{{ plugin_category }}_{{ plugin_name }}_{arch.arch}.so")
         if plugin_path.exists():
             shutil.copyfile(plugin_path,
                             (Path(self.ctx.get_libs_dir(arch.arch)) /
                              f"libplugins_{{ plugin_category }}_{{ plugin_name }}_{arch.arch}.so"))
-        {% endfor %}
+        {% endfor %}  # noqa: E999
 
 
 recipe = PySideRecipe()
index f6898ad72e14aed6a97530784be144111874c483..5dbaa68eba1ab922de04816ddebc9b265316fd11 100644 (file)
@@ -10,13 +10,11 @@ from pathlib import Path
 from project import ProjectData
 
 from .commands import run_qmlimportscanner
+from . import DEFAULT_APP_ICON
 
 # Some QML plugins like QtCore are excluded from this list as they don't contribute much to
 # executable size. Excluding them saves the extra processing of checking for them in files
 EXCLUDED_QML_PLUGINS = {"QtQuick", "QtQuick3D", "QtCharts", "QtWebEngine", "QtTest", "QtSensors"}
-# TODO: Move this to android module. Fix circular import.
-ANDROID_NDK_VERSION = "25c"
-ANDROID_DEPLOY_CACHE = Path.home() / ".pyside6_android_deploy"
 
 
 class BaseConfig:
@@ -63,14 +61,15 @@ class Config(BaseConfig):
     """
 
     def __init__(self, config_file: Path, source_file: Path, python_exe: Path, dry_run: bool,
-                 android_data, is_android: bool, existing_config_file: bool = False):
+                 existing_config_file: bool = False):
         super().__init__(config_file=config_file, existing_config_file=existing_config_file)
 
         self._dry_run = dry_run
+        self.qml_modules = set()
         # set source_file
         self.source_file = Path(
             self.set_or_fetch(config_property_val=source_file, config_property_key="input_file")
-        )
+        ).resolve()
 
         # set python path
         self.python_path = Path(
@@ -83,6 +82,13 @@ class Config(BaseConfig):
 
         self.title = self.get_value("app", "title")
 
+        # set application icon
+        config_icon = self.get_value("app", "icon")
+        if config_icon:
+            self.icon = str(Path(config_icon).resolve())
+        else:
+            self.icon = DEFAULT_APP_ICON
+
         self.project_dir = None
         if self.get_value("app", "project_dir"):
             self.project_dir = Path(self.get_value("app", "project_dir")).absolute()
@@ -115,83 +121,7 @@ class Config(BaseConfig):
         else:
             self._find_and_set_excluded_qml_plugins()
 
-        # Android
-        if is_android:
-            if android_data.wheel_pyside:
-                self.wheel_pyside = android_data.wheel_pyside
-            else:
-                wheel_pyside_temp = self.get_value("qt", "wheel_pyside")
-                if not wheel_pyside_temp:
-                    raise RuntimeError("[DEPLOY] Unable to find PySide6 Android wheel")
-                self.wheel_pyside = Path(wheel_pyside_temp).resolve()
-
-            if android_data.wheel_shiboken:
-                self.wheel_shiboken = android_data.wheel_shiboken
-            else:
-                wheel_shiboken_temp = self.get_value("qt", "wheel_shiboken")
-                if not wheel_shiboken_temp:
-                    raise RuntimeError("[DEPLOY] Unable to find shiboken6 Android wheel")
-                self.wheel_shiboken = Path(wheel_shiboken_temp).resolve()
-
-            self.ndk_path = None
-            if android_data.ndk_path:
-                # from cli
-                self.ndk_path = android_data.ndk_path
-            else:
-                # from config
-                ndk_path_temp = self.get_value("buildozer", "ndk_path")
-                if ndk_path_temp:
-                    self.ndk_path = Path(ndk_path_temp)
-                else:
-                    ndk_path_temp = (ANDROID_DEPLOY_CACHE / "android-ndk"
-                                     / f"android-ndk-r{ANDROID_NDK_VERSION}")
-                    if ndk_path_temp.exists():
-                        self.ndk_path = ndk_path_temp
-
-            if self.ndk_path:
-                print(f"Using Android NDK: {str(self.ndk_path)}")
-            else:
-                raise FileNotFoundError("[DEPLOY] Unable to find Android NDK. Please pass the NDK "
-                                        "path either from the CLI or from pysidedeploy.spec")
-
-            self.sdk_path = None
-            if android_data.sdk_path:
-                self.sdk_path = android_data.sdk_path
-            else:
-                sdk_path_temp = self.get_value("buildozer", "sdk_path")
-                if sdk_path_temp:
-                    self.sdk_path = Path(sdk_path_temp)
-                else:
-                    sdk_path_temp = ANDROID_DEPLOY_CACHE / "android-sdk"
-                    if sdk_path_temp.exists():
-                        self.sdk_path = sdk_path_temp
-                    else:
-                        logging.info("[DEPLOY] Use default SDK from buildozer")
-
-            if self.sdk_path:
-                print(f"Using Android SDK: {str(self.sdk_path)}")
-
-            recipe_dir_temp = self.get_value("buildozer", "recipe_dir")
-            self.recipe_dir = Path(recipe_dir_temp) if recipe_dir_temp else None
-
-            jars_dir_temp = self.get_value("buildozer", "jars_dir")
-            self.jars_dir = Path(jars_dir_temp) if jars_dir_temp else None
-
-            self._modules = []
-            if self.get_value("buildozer", "modules"):
-                self.modules = self.get_value("buildozer", "modules").split(",")
-
-            self.arch = self.get_value("buildozer", "arch")
-
-            self._local_libs = []
-            if self.get_value("buildozer", "local_libs"):
-                self.local_libs = self.get_value("buildozer", "local_libs").split(",")
-
-            self._qt_plugins = []
-            if self.get_value("qt", "plugins"):
-                self._qt_plugins = self.get_value("qt", "plugins").split(",")
-
-            self._mode = self.get_value("buildozer", "mode")
+        self._generated_files_path = self.project_dir / "deployment"
 
     def set_or_fetch(self, config_property_val, config_property_key, config_property_group="app"):
         """
@@ -214,6 +144,10 @@ class Config(BaseConfig):
     def dry_run(self):
         return self._dry_run
 
+    @property
+    def generated_files_path(self):
+        return self._generated_files_path
+
     @property
     def qml_files(self):
         return self._qml_files
@@ -239,6 +173,15 @@ class Config(BaseConfig):
         self._title = title
         self.set_value("app", "title", title)
 
+    @property
+    def icon(self):
+        return self._icon
+
+    @icon.setter
+    def icon(self, icon):
+        self._icon = icon
+        self.set_value("app", "icon", icon)
+
     @property
     def source_file(self):
         return self._source_file
@@ -271,95 +214,6 @@ class Config(BaseConfig):
     def excluded_qml_plugins(self, excluded_qml_plugins):
         self._excluded_qml_plugins = excluded_qml_plugins
 
-    @property
-    def recipe_dir(self):
-        return self._recipe_dir
-
-    @recipe_dir.setter
-    def recipe_dir(self, recipe_dir: Path):
-        self._recipe_dir = recipe_dir.resolve() if recipe_dir else None
-        if self._recipe_dir:
-            self.set_value("buildozer", "recipe_dir", str(self._recipe_dir))
-
-    def recipes_exist(self):
-        if not self._recipe_dir:
-            return False
-
-        pyside_recipe_dir = Path(self.recipe_dir) / "PySide6"
-        shiboken_recipe_dir = Path(self.recipe_dir) / "shiboken6"
-
-        return pyside_recipe_dir.is_dir() and shiboken_recipe_dir.is_dir()
-
-    @property
-    def jars_dir(self) -> Path:
-        return self._jars_dir
-
-    @jars_dir.setter
-    def jars_dir(self, jars_dir: Path):
-        self._jars_dir = jars_dir.resolve() if jars_dir else None
-        if self._jars_dir:
-            self.set_value("buildozer", "jars_dir", str(self._jars_dir))
-
-    @property
-    def modules(self):
-        return self._modules
-
-    @modules.setter
-    def modules(self, modules):
-        self._modules = modules
-        self.set_value("buildozer", "modules", ",".join(modules))
-
-    @property
-    def local_libs(self):
-        return self._local_libs
-
-    @local_libs.setter
-    def local_libs(self, local_libs):
-        self._local_libs = local_libs
-        self.set_value("buildozer", "local_libs", ",".join(local_libs))
-
-    @property
-    def qt_plugins(self):
-        return self._qt_plugins
-
-    @qt_plugins.setter
-    def qt_plugins(self, qt_plugins):
-        self._qt_plugins = qt_plugins
-        self.set_value("qt", "plugins", ",".join(qt_plugins))
-
-    @property
-    def ndk_path(self):
-        return self._ndk_path
-
-    @ndk_path.setter
-    def ndk_path(self, ndk_path: Path):
-        self._ndk_path = ndk_path.resolve() if ndk_path else None
-        if self._ndk_path:
-            self.set_value("buildozer", "ndk_path", str(self._ndk_path))
-
-    @property
-    def sdk_path(self) -> Path:
-        return self._sdk_path
-
-    @sdk_path.setter
-    def sdk_path(self, sdk_path: Path):
-        self._sdk_path = sdk_path.resolve() if sdk_path else None
-        if self._sdk_path:
-            self.set_value("buildozer", "sdk_path", str(self._sdk_path))
-
-    @property
-    def arch(self):
-        return self._arch
-
-    @arch.setter
-    def arch(self, arch):
-        self._arch = arch
-        self.set_value("buildozer", "arch", arch)
-
-    @property
-    def mode(self):
-        return self._mode
-
     @property
     def exe_dir(self):
         return self._exe_dir
@@ -368,26 +222,6 @@ class Config(BaseConfig):
     def exe_dir(self, exe_dir: Path):
         self._exe_dir = exe_dir
 
-    @property
-    def wheel_pyside(self) -> Path:
-        return self._wheel_pyside
-
-    @wheel_pyside.setter
-    def wheel_pyside(self, wheel_pyside: Path):
-        self._wheel_pyside = wheel_pyside.resolve() if wheel_pyside else None
-        if self._wheel_pyside:
-            self.set_value("qt", "wheel_pyside", str(self._wheel_pyside))
-
-    @property
-    def wheel_shiboken(self) -> Path:
-        return self._wheel_shiboken
-
-    @wheel_shiboken.setter
-    def wheel_shiboken(self, wheel_shiboken: Path):
-        self._wheel_shiboken = wheel_shiboken.resolve() if wheel_shiboken else None
-        if self._wheel_shiboken:
-            self.set_value("qt", "wheel_shiboken", str(self._wheel_shiboken))
-
     def _find_and_set_qml_files(self):
         """Fetches all the qml_files in the folder and sets them if the
         field qml_files is empty in the config_dir"""
@@ -478,9 +312,9 @@ class Config(BaseConfig):
 
     def _find_and_set_excluded_qml_plugins(self):
         if self.qml_files:
-            included_qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
-                                                            dry_run=self.dry_run))
-            self.excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(included_qml_modules)
+            self.qml_modules = set(run_qmlimportscanner(qml_files=self.qml_files,
+                                                        dry_run=self.dry_run))
+            self.excluded_qml_plugins = EXCLUDED_QML_PLUGINS.difference(self.qml_modules)
 
             # needed for dry_run testing
             self.excluded_qml_plugins = sorted(self.excluded_qml_plugins)
index c49ef53bfc6cb62e67981b4e2b8aba0155c4cf1a..7ca2edfe7986984e1d2a05a74e99460a9e951279 100644 (file)
@@ -16,6 +16,9 @@ exec_directory =
 # Path to .pyproject project file
 project_file =
 
+# Application icon
+icon =
+
 [python]
 
 # Python path
@@ -24,7 +27,7 @@ python_path =
 # python packages to install
 # ordered-set: increase compile time performance of nuitka packaging
 # zstandard: provides final executable size optimization
-packages = nuitka==1.5.4,ordered_set,zstandard
+packages = nuitka==1.8.0,ordered_set,zstandard
 
 # buildozer: for deploying Android application
 android_packages = buildozer==1.5.0,cython==0.29.33
@@ -32,12 +35,14 @@ android_packages = buildozer==1.5.0,cython==0.29.33
 [qt]
 
 # Comma separated path to QML files required
-# normally all the QML files are added automatically
+# normally all the QML files required by the project are added automatically
 qml_files =
 
 # excluded qml plugin binaries
 excluded_qml_plugins =
 
+[android]
+
 # path to PySide wheel
 wheel_pyside =
 
@@ -51,7 +56,7 @@ plugins = platforms_qtforandroid
 
 # (str) specify any extra nuitka arguments
 # eg: extra_args = --show-modules --follow-stdlib
-extra_args = --quiet --noinclude-qt-translations=True
+extra_args = --quiet --noinclude-qt-translations
 
 [buildozer]
 
index a73ccb0ebce7b94fd01738436acb42058886c7e8..a8ca586118f5dc86f1225a799d5ed48405250379 100644 (file)
@@ -19,12 +19,12 @@ def config_option_exists():
     return False
 
 
-def cleanup(generated_files_path: Path, config: Config, is_android: bool = False):
+def cleanup(config: Config, is_android: bool = False):
     """
         Cleanup the generated build folders/files
     """
-    if generated_files_path.exists():
-        shutil.rmtree(generated_files_path)
+    if config.generated_files_path.exists():
+        shutil.rmtree(config.generated_files_path)
         logging.info("[DEPLOY] Deployment directory purged")
 
     if is_android:
@@ -39,37 +39,27 @@ def cleanup(generated_files_path: Path, config: Config, is_android: bool = False
             logging.info(f"[DEPLOY] {str(buildozer_build)} removed")
 
 
-def get_config(python_exe: Path, dry_run: bool = False, config_file: Path = None, main_file:
-               Path = None, android_data=None, is_android: bool = False):
+def create_config_file(dry_run: bool = False, config_file: Path = None, main_file: Path = None):
     """
         Sets up a new pysidedeploy.spec or use an existing config file
     """
 
-    config_file_exists = config_file and Path(config_file).exists()
-
-    if main_file and not config_file_exists:
+    if main_file:
         if main_file.parent != Path.cwd():
             config_file = main_file.parent / "pysidedeploy.spec"
         else:
             config_file = Path.cwd() / "pysidedeploy.spec"
 
-    if config_file_exists:
-        logging.info(f"[DEPLOY] Using existing config file {config_file}")
-    else:
-        logging.info(f"[DEPLOY] Creating config file {config_file}")
-        if not dry_run:
-            shutil.copy(Path(__file__).parent / "default.spec", config_file)
+    logging.info(f"[DEPLOY] Creating config file {config_file}")
+    if not dry_run:
+        shutil.copy(Path(__file__).parent / "default.spec", config_file)
 
     # the config parser needs a reference to parse. So, in the case of --dry-run
     # use the default.spec file.
-    if dry_run and not config_file_exists:
+    if dry_run:
         config_file = Path(__file__).parent / "default.spec"
 
-    config = Config(config_file=config_file, source_file=main_file, python_exe=python_exe,
-                    dry_run=dry_run, android_data=android_data, is_android=is_android,
-                    existing_config_file=config_file_exists)
-
-    return config
+    return config_file
 
 
 def setup_python(dry_run: bool, force: bool, init: bool):
@@ -99,22 +89,28 @@ def install_python_dependencies(config: Config, python: PythonExecutable, init:
     """
         Installs the python package dependencies for the target deployment platform
     """
+    packages = config.get_value("python", packages).split(",")
     if not init:
         # install packages needed for deployment
         logging.info("[DEPLOY] Installing dependencies")
-        packages = config.get_value("python", packages).split(",")
         python.install(packages=packages)
         # nuitka requires patchelf to make patchelf rpath changes for some Qt files
         if sys.platform.startswith("linux") and not is_android:
             python.install(packages=["patchelf"])
+    elif is_android:
+        # install only buildozer
+        logging.info("[DEPLOY] Installing buildozer")
+        buildozer_package_with_version = ([package for package in packages
+                                          if package.startswith("buildozer")])
+        python.install(packages=list(buildozer_package_with_version))
 
 
-def finalize(generated_files_path: Path, config: Config):
+def finalize(config: Config):
     """
         Copy the executable into the final location
         For Android deployment, this is done through buildozer
     """
-    generated_exec_path = generated_files_path / (config.source_file.stem + EXE_FORMAT)
+    generated_exec_path = config.generated_files_path / (config.source_file.stem + EXE_FORMAT)
     if generated_exec_path.exists() and config.exe_dir:
         shutil.copy(generated_exec_path, config.exe_dir)
         print("[DEPLOY] Executed file created in "
index ae3d2ff946b4cc5be856d58fc82f1338277464c1..ae5834b6bd8651f391749e85f7ca0116bff1a3ae 100644 (file)
@@ -18,8 +18,17 @@ class Nuitka:
     def __init__(self, nuitka):
         self.nuitka = nuitka
 
+    @staticmethod
+    def icon_option():
+        if sys.platform == "linux":
+            return "--linux-icon"
+        elif sys.platform == "win32":
+            return "--windows-icon-from-ico"
+        else:
+            return "--macos-app-icon"
+
     def create_executable(self, source_file: Path, extra_args: str, qml_files: List[Path],
-                          excluded_qml_plugins, dry_run: bool):
+                          excluded_qml_plugins: List[str], icon: str, dry_run: bool):
         extra_args = extra_args.split()
         qml_args = []
         if qml_files:
@@ -51,10 +60,7 @@ class Nuitka:
             f"--output-dir={output_dir}",
         ]
         command.extend(extra_args + qml_args)
-
-        if sys.platform == "linux":
-            linux_icon = str(Path(__file__).parent / "pyside_icon.jpg")
-            command.append(f"--linux-onefile-icon={linux_icon}")
+        command.append(f"{self.__class__.icon_option()}={icon}")
 
         command_str, _ = run_command(command=command, dry_run=dry_run)
         return command_str
diff --git a/sources/pyside-tools/deploy_lib/pyside_icon.icns b/sources/pyside-tools/deploy_lib/pyside_icon.icns
new file mode 100644 (file)
index 0000000..a6eb02b
Binary files /dev/null and b/sources/pyside-tools/deploy_lib/pyside_icon.icns differ
diff --git a/sources/pyside-tools/deploy_lib/pyside_icon.ico b/sources/pyside-tools/deploy_lib/pyside_icon.ico
new file mode 100644 (file)
index 0000000..332a3a5
Binary files /dev/null and b/sources/pyside-tools/deploy_lib/pyside_icon.ico differ
index f403b2d47e68eb537c84817b740b34f043e284de..6ec3b64f87d47c17ed3a87c85fe7d067df7295c7 100644 (file)
@@ -12,7 +12,7 @@ from importlib import util
 from importlib.metadata import version
 from pathlib import Path
 
-from . import Config, Nuitka, run_command
+from . import Nuitka, run_command
 
 IMPORT_WARNING_PYSIDE = (f"[DEPLOY] Found 'import PySide6' in file {0}"
                          ". Use 'from PySide6 import <module>' or pass the module"
@@ -155,8 +155,8 @@ class PythonExecutable:
         return False
 
     def install(self, packages: list = None):
-        _, installed_packages = run_command(command=[str(self.exe), "-m", "pip", "freeze"], dry_run=False
-                                            , fetch_output=True)
+        _, installed_packages = run_command(command=[str(self.exe), "-m", "pip", "freeze"],
+                                            dry_run=False, fetch_output=True)
         installed_packages = [p.decode().split('==')[0] for p in installed_packages.split()]
         for package in packages:
             package_info = package.split('==')
@@ -193,16 +193,16 @@ class PythonExecutable:
     def is_installed(self, package):
         return bool(util.find_spec(package))
 
-    def create_executable(self, source_file: Path, extra_args: str, config: Config):
+    def create_executable(self, source_file: Path, extra_args: str, config):
         if config.qml_files:
             logging.info(f"[DEPLOY] Included QML files: {config.qml_files}")
 
-        command_str = self.nuitka.create_executable(
-                        source_file=source_file,
-                        extra_args=extra_args,
-                        qml_files=config.qml_files,
-                        excluded_qml_plugins=config.excluded_qml_plugins,
-                        dry_run=self.dry_run,
-                    )
+        command_str = self.nuitka.create_executable(source_file=source_file,
+                                                    extra_args=extra_args,
+                                                    qml_files=config.qml_files,
+                                                    excluded_qml_plugins=(config.
+                                                                          excluded_qml_plugins),
+                                                    icon=config.icon,
+                                                    dry_run=self.dry_run)
 
         return command_str
index 3dbe4254711cdda28b7636707e699e197a5d75d7..621bf1ac75950a570f59f3145801d371329958d2 100644 (file)
@@ -51,6 +51,7 @@ NEW_PROJECT_TYPES = {"new-quick": ProjectType.QUICK,
                      "new-ui": ProjectType.WIDGET_FORM,
                      "new-widget": ProjectType.WIDGET}
 
+
 class Project:
     """
     Class to wrap the various operations on Project
@@ -117,7 +118,7 @@ class Project:
             qmltypes_file = self._qml_module_dir / f"{stem}.qmltypes"
             cpp_file = self._qml_module_dir / f"{stem}_qmltyperegistrations.cpp"
             cmd = [QMLTYPEREGISTRAR_CMD, "--generate-qmltypes",
-                   os.fspath(qmltypes_file),  "-o", os.fspath(cpp_file),
+                   os.fspath(qmltypes_file), "-o", os.fspath(cpp_file),
                    os.fspath(file)]
             cmd.extend(self._qml_project_data.registrar_options())
             return ([qmltypes_file, cpp_file], cmd)
index 5211c397990f6b3b8c4f0373ad4908cc738ef23a..c363a9fc0b395b9d054b114fd167c484c04bf396 100644 (file)
@@ -145,7 +145,7 @@ def _qml_project() -> Project:
 
 
 def new_project(directory_s: str,
-                project_type: ProjectType=ProjectType.WIDGET_FORM) -> int:
+                project_type: ProjectType = ProjectType.WIDGET_FORM) -> int:
     directory = Path(directory_s)
     if directory.exists():
         print(f"{directory_s} already exists.", file=sys.stderr)
index b8d27f33e485a165507a8d4b25db468339939201..416089dce0db90ee20f0cfff6b50e0998e2826d3 100644 (file)
@@ -11,6 +11,7 @@ from . import (METATYPES_JSON_SUFFIX, PROJECT_FILE_SUFFIX, qt_metatype_json_dir,
                MOD_CMD, QML_IMPORT_MAJOR_VERSION, QML_IMPORT_MINOR_VERSION, QML_IMPORT_NAME,
                QT_MODULES)
 
+
 def is_python_file(file: Path) -> bool:
     return (file.suffix == ".py"
             or sys.platform == "win32" and file.suffix == ".pyw")
@@ -106,7 +107,7 @@ class ProjectData:
 
         # __main__ not found
         print(
-            "Python file with main function not found. Add the file to" f" {project_file}",
+            "Python file with main function not found. Add the file to" f" {self.project_file}",
             file=sys.stderr,
         )
         sys.exit(1)
@@ -233,4 +234,3 @@ def check_qml_decorators(py_file: Path) -> Tuple[bool, QmlProjectData]:
         if v:
             qml_project_data.qt_modules = v
     return (has_class, qml_project_data)
-
index 1648b2943054afec66ab765726a4aab9041a8f70..7daacc22d5f2289ebe3ee08c34647510687eabcc 100644 (file)
@@ -8,13 +8,23 @@ import subprocess
 import sys
 import sysconfig
 from pathlib import Path
-from subprocess import PIPE, Popen
 
 import PySide6 as ref_mod
 
 VIRTUAL_ENV = "VIRTUAL_ENV"
 
 
+def is_pyenv_python():
+    pyenv_root = os.environ.get("PYENV_ROOT")
+
+    if pyenv_root:
+        resolved_exe = Path(sys.executable).resolve()
+        if str(resolved_exe).startswith(pyenv_root):
+            return True
+
+    return False
+
+
 def is_virtual_env():
     return sys.prefix != sys.base_prefix
 
@@ -46,13 +56,11 @@ def qt_tool_wrapper(qt_tool, args, libexec=False):
         exe = pyside_dir / qt_tool
 
     cmd = [os.fspath(exe)] + args
-    proc = Popen(cmd, stderr=PIPE)
-    out, err = proc.communicate()
-    if err:
-        msg = err.decode("utf-8")
+    returncode = subprocess.call(cmd)
+    if returncode != 0:
         command = ' '.join(cmd)
-        print(f"Error: {msg}\nwhile executing '{command}'")
-    sys.exit(proc.returncode)
+        print(f"'{command}' returned {returncode}", file=sys.stderr)
+    sys.exit(returncode)
 
 
 def pyside_script_wrapper(script_name):
@@ -147,11 +155,23 @@ def designer():
         # Determine library name (examples/utils/pyside_config.py)
         version = f'{major_version}.{minor_version}'
         library_name = f'libpython{version}{sys.abiflags}.so'
+        if is_pyenv_python():
+            library_name = str(Path(sysconfig.get_config_var('LIBDIR')) / library_name)
         os.environ['LD_PRELOAD'] = library_name
     elif sys.platform == 'darwin':
         library_name = sysconfig.get_config_var("LDLIBRARY")
         framework_prefix = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")
-        lib_path = os.fspath(Path(framework_prefix) / library_name)
+        lib_path = None
+        if framework_prefix:
+            lib_path = os.fspath(Path(framework_prefix) / library_name)
+        elif is_pyenv_python():
+            lib_path = str(Path(sysconfig.get_config_var('LIBDIR')) / library_name)
+        else:
+            # ideally this should never be reached because the system Python and Python installed
+            # from python.org are all framework builds
+            print("Unable to find Python library directory. Use a framework build of Python.",
+                  file=sys.stderr)
+            sys.exit(0)
         os.environ['DYLD_INSERT_LIBRARIES'] = lib_path
     elif sys.platform == 'win32':
         # Find Python DLLs from the base installation
@@ -197,11 +217,13 @@ def android_deploy():
     if not sys.platform == "linux":
         print("pyside6-android-deploy only works from a Linux host")
     else:
-        dependent_packages = ["jinja2", "pkginfo"]
-        for dependent_package in dependent_packages:
-            if not bool(importlib.util.find_spec(dependent_package)):
-                command = [sys.executable, "-m", "pip", "install", dependent_package]
-                subprocess.run(command)
+        android_requirements_file = Path(__file__).parent / "requirements-android.txt"
+        with open(android_requirements_file, 'r', encoding='UTF-8') as file:
+            while line := file.readline():
+                dependent_package = line.rstrip()
+                if not bool(importlib.util.find_spec(dependent_package)):
+                    command = [sys.executable, "-m", "pip", "install", dependent_package]
+                    subprocess.run(command)
         pyside_script_wrapper("android_deploy.py")
 
 
index 61e0e8ff9c25cf855ed622e732dea4f445a2d7d8..d76e5c5c9812ca945a2e745f3060302792ae4c09 100644 (file)
@@ -215,7 +215,7 @@ if __name__ == "__main__":
     if apptype == "core":
         component = QQmlComponent(engine, qml_file)
         obj = component.create()
-        filtered_attributes = {k: v for k, v in vars(obj).items() if type(v) != SignalInstance}
+        filtered_attributes = {k: v for k, v in vars(obj).items() if type(v) is not SignalInstance}
         logging.info("qml: component object attributes are")
         pprint(filtered_attributes)
         del engine
@@ -227,7 +227,7 @@ if __name__ == "__main__":
         sys.exit(-1)
 
     qquick_view = False
-    if type(rootObjects[0]) != QQuickWindow and qquick_present:
+    if isinstance(rootObjects[0], QQuickWindow) and qquick_present:
         logging.info("qml: loading with QQuickView")
         viewer = QQuickView()
         viewer.setSource(qml_file)
index 10dc73767a8a13cda1cdfe107fca0d7b355142da..1466ac6b122ecc8a9263c26180e58c8a51bbbbd9 100644 (file)
@@ -4,7 +4,7 @@
 
 import sys
 
-from PySide6.QtCore import qVersion, Qt
+from PySide6.QtCore import Qt
 from PySide6.QtGui import QColor, QPainter, QPaintEvent, QShortcut
 from PySide6.QtWidgets import QApplication, QWidget
 
index f9f921705f0235ea395dea5779b7114f47670c00..894b2a958397218ee822e41b36f6b376bf791826 100644 (file)
@@ -14,7 +14,7 @@ def diff_code(actual_code, expected_file):
     with tempfile.NamedTemporaryFile(suffix=".cpp") as tf:
         tf.write(actual_code.encode('utf-8'))
         tf.flush()
-        diff_cmd = ["diff", "-u", expected_file,  tf.name]
+        diff_cmd = ["diff", "-u", expected_file, tf.name]
         subprocess.run(diff_cmd)
 
 
@@ -33,22 +33,22 @@ def run_converter(tool, file):
 def test_examples():
     dir = Path(__file__).resolve().parent
     tool = dir.parents[1] / "qtpy2cpp.py"
-    assert(tool.is_file)
+    assert tool.is_file
     for test_file in (dir / "baseline").glob("*.py"):
-        assert(test_file.is_file)
+        assert test_file.is_file
         expected_file = test_file.parent / (test_file.stem + ".cpp")
         if expected_file.is_file():
             actual_code = run_converter(tool, test_file)
-            assert(actual_code)
+            assert actual_code
             expected_code = expected_file.read_text()
             # Strip the license
             code_start = expected_code.find("// Converted from")
-            assert(code_start != -1)
+            assert code_start != -1
             expected_code = expected_code[code_start:]
 
             if actual_code != expected_code:
                 diff_code(actual_code, expected_file)
-            assert(actual_code == expected_code)
+            assert actual_code == expected_code
         else:
             print(f"Warning, {test_file} is missing a .cpp file.",
                   file=sys.stderr)
index 1e8b5dc84bd4f528366b57e2cf09474005ac2a70..2056951ae87a7b848402ad068f092a7f70ae54ca 100644 (file)
@@ -10,8 +10,7 @@ import warnings
 
 from .formatter import (CppFormatter, format_for_loop, format_literal,
                         format_name_constant,
-                        format_reference, format_start_function_call,
-                        write_import, write_import_from)
+                        format_reference, write_import, write_import_from)
 from .nodedump import debug_format_node
 from .qt import ClassFlag, qt_class_flags
 
diff --git a/sources/pyside-tools/requirements-android.txt b/sources/pyside-tools/requirements-android.txt
new file mode 100644 (file)
index 0000000..1169fd6
--- /dev/null
@@ -0,0 +1,2 @@
+jinja2
+pkginfo
index ec46c853e8c738b36ff753a9bb7aab3223a9853b..66542fa558f1f95f73108116a2ead1f7c9d7127e 100644 (file)
@@ -1,5 +1,5 @@
 set(pyside_MAJOR_VERSION "6")
 set(pyside_MINOR_VERSION "6")
-set(pyside_MICRO_VERSION "1")
+set(pyside_MICRO_VERSION "2")
 set(pyside_PRE_RELEASE_VERSION_TYPE "")
 set(pyside_PRE_RELEASE_VERSION "")
index 2e4c7010d16ae2af53e2ffe3c1ec31a947e5143b..f45c071148b53dbdbea759a2972a4e6fbad8e134 100644 (file)
@@ -14,9 +14,9 @@ include(cmake/PySideSetup.cmake)
 get_rpath_base_token(base)
 
 if (${STANDALONE})
-    set(CMAKE_INSTALL_RPATH ${base}/ ${base}/Qt/lib)
+    set(CMAKE_INSTALL_RPATH ${base}/ ${base}/Qt/lib ${base}/../shiboken6/)
 else()
-    set(CMAKE_INSTALL_RPATH ${base}/ ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS})
+    set(CMAKE_INSTALL_RPATH ${base}/ ${QT6_INSTALL_PREFIX}/${QT6_INSTALL_LIBS} ${base}/../shiboken6/)
 endif()
 
 add_subdirectory(libpyside)
index 2c673e405ad4fc29b5488a25d02e9af78084f4af..3830ad887f953ee3fc6a41274aa05eee7555583b 100644 (file)
@@ -7,8 +7,41 @@ from .events import (
 from .futures import QAsyncioFuture
 from .tasks import QAsyncioTask
 
+import asyncio
+import typing
+
 __all__ = [
     "QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
     "QAsyncioHandle", "QAsyncioTimerHandle",
     "QAsyncioFuture", "QAsyncioTask"
 ]
+
+
+def run(coro: typing.Optional[typing.Coroutine] = None,
+        keep_running: bool = True,
+        quit_qapp: bool = True, *,
+        debug: typing.Optional[bool] = None) -> None:
+    """Run the QtAsyncio event loop."""
+
+    # Event loop policies are expected to be deprecated with Python 3.13, with
+    # subsequent removal in Python 3.15. At that point, part of the current
+    # logic of the QAsyncioEventLoopPolicy constructor will have to be moved
+    # here and/or to a loop factory class (to be provided as an argument to
+    # asyncio.run()), namely setting up the QCoreApplication and the SIGINT
+    # handler.
+    #
+    # More details:
+    # https://discuss.python.org/t/removing-the-asyncio-policy-system-asyncio-set-event-loop-policy-in-python-3-15/37553  # noqa: E501
+    asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy(quit_qapp=quit_qapp))
+
+    if keep_running:
+        if coro:
+            asyncio.ensure_future(coro)
+        asyncio.get_event_loop().run_forever()
+    else:
+        if coro:
+            asyncio.run(coro, debug=debug)
+        else:
+            raise RuntimeError(
+                "QtAsyncio was set to keep running after the coroutine "
+                "finished, but no coroutine was provided.")
index edd42646f3be5c5b7f979ba01e4d9cf9d8583882..aa89e984ef1816470684e9a5f1ee59576a781be7 100644 (file)
@@ -1,7 +1,8 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
-from PySide6.QtCore import QCoreApplication, QDateTime, QEventLoop, QObject, QTimer, QThread, Slot
+from PySide6.QtCore import (QCoreApplication, QDateTime, QDeadlineTimer,
+                            QEventLoop, QObject, QTimer, QThread, Slot)
 
 from . import futures
 from . import tasks
@@ -12,9 +13,11 @@ import concurrent.futures
 import contextvars
 import enum
 import os
+import signal
 import socket
 import subprocess
 import typing
+import warnings
 
 __all__ = [
     "QAsyncioEventLoopPolicy", "QAsyncioEventLoop",
@@ -54,7 +57,9 @@ class QAsyncioExecutorWrapper(QObject):
 
 
 class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
-    def __init__(self, application: typing.Optional[QCoreApplication] = None) -> None:
+    def __init__(self,
+                 application: typing.Optional[QCoreApplication] = None,
+                 quit_qapp: bool = True) -> None:
         super().__init__()
         if application is None:
             if QCoreApplication.instance() is None:
@@ -62,8 +67,11 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
             else:
                 application = QCoreApplication.instance()
         self._application: QCoreApplication = application  # type: ignore[assignment]
+        self._quit_qapp = quit_qapp
         self._event_loop: typing.Optional[asyncio.AbstractEventLoop] = None
 
+        signal.signal(signal.SIGINT, signal.SIG_DFL)
+
     def get_event_loop(self) -> asyncio.AbstractEventLoop:
         if self._event_loop is None:
             self._event_loop = QAsyncioEventLoop(self._application)
@@ -73,7 +81,13 @@ class QAsyncioEventLoopPolicy(asyncio.AbstractEventLoopPolicy):
         self._event_loop = loop
 
     def new_event_loop(self) -> asyncio.AbstractEventLoop:
-        return QAsyncioEventLoop(self._application)
+        return QAsyncioEventLoop(self._application, quit_qapp=self._quit_qapp)
+
+    def get_child_watcher(self) -> asyncio.AbstractChildWatcher:
+        raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
+
+    def set_child_watcher(self, watcher: asyncio.AbstractChildWatcher) -> None:
+        raise DeprecationWarning("Child watchers are deprecated since Python 3.12")
 
 
 class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
@@ -101,11 +115,13 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
                 if not self._loop.is_closed():
                     self._loop.call_soon_threadsafe(self._future.set_exception, e)
 
-    def __init__(self, application: QCoreApplication) -> None:
+    def __init__(self,
+                 application: QCoreApplication, quit_qapp: bool = True) -> None:
         asyncio.BaseEventLoop.__init__(self)
         QObject.__init__(self)
 
         self._application: QCoreApplication = application
+        self._quit_qapp = quit_qapp
         self._thread = QThread.currentThread()
 
         self._closed = False
@@ -135,7 +151,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
                 return
         future.get_loop().stop()
 
-    def run_until_complete(self, future: futures.QAsyncioFuture) -> typing.Any:  # type: ignore[override]
+    def run_until_complete(self,
+                           future: futures.QAsyncioFuture) -> typing.Any:  # type: ignore[override]
         if self.is_closed():
             raise RuntimeError("Event loop is closed")
         if self.is_running():
@@ -179,7 +196,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
             else:
                 return
         self._quit_from_inside = True
-        self._application.quit()
+        if self._quit_qapp:
+            self._application.quit()
 
     def is_running(self) -> bool:
         return self._thread.loopLevel() > 0
@@ -194,7 +212,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
             return
         if self._default_executor is not None:
             self._default_executor.shutdown(wait=False)
-        self._application.shutdown()
+        if self._quit_qapp:
+            self._application.shutdown()
         self._closed = True
 
     async def shutdown_asyncgens(self) -> None:
@@ -215,7 +234,14 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
 
         self._asyncgens.clear()
 
-    async def shutdown_default_executor(self) -> None:
+    async def shutdown_default_executor(self,  # type: ignore[override]
+                                        timeout: typing.Union[int, float, None] = None) -> None:
+        shutdown_successful = False
+        if timeout is not None:
+            deadline_timer = QDeadlineTimer(int(timeout * 1000))
+        else:
+            deadline_timer = QDeadlineTimer(QDeadlineTimer.Forever)
+
         if self._default_executor is None:
             return
         future = self.create_future()
@@ -224,39 +250,61 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
         try:
             await future
         finally:
-            thread.wait()
+            shutdown_successful = thread.wait(deadline_timer)
+
+        if timeout is not None and not shutdown_successful:
+            warnings.warn(
+                f"Could not shutdown the default executor within {timeout} seconds",
+                RuntimeWarning, stacklevel=2)
+            self._default_executor.shutdown(wait=False)
 
     # Scheduling callbacks
 
+    def _call_soon_impl(self, callback: typing.Callable, *args: typing.Any,
+                        context: typing.Optional[contextvars.Context] = None,
+                        is_threadsafe: typing.Optional[bool] = False) -> asyncio.Handle:
+        return self._call_later_impl(0, callback, *args, context=context,
+                                     is_threadsafe=is_threadsafe)
+
     def call_soon(self, callback: typing.Callable, *args: typing.Any,
-                  context: typing.Optional[contextvars.Context] = None):
-        return self.call_later(0, callback, *args, context=context)
+                  context: typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
+        return self._call_soon_impl(callback, *args, context=context, is_threadsafe=False)
 
-    def call_soon_threadsafe(self, callback: typing.Callable,  # type: ignore[override]
-                             *args: typing.Any,
+    def call_soon_threadsafe(self, callback: typing.Callable, *args: typing.Any,
                              context:
-                             typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle":
-        if self.is_closed():
-            raise RuntimeError("Event loop is closed")
+                             typing.Optional[contextvars.Context] = None) -> asyncio.Handle:
         if context is None:
             context = contextvars.copy_context()
-        return self.call_soon(callback, *args, context=context)
+        return self._call_soon_impl(callback, *args, context=context, is_threadsafe=True)
 
-    def call_later(self, delay: typing.Union[int, float],  # type: ignore[override]
-                   callback: typing.Callable, *args: typing.Any,
-                   context: typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle":
+    def _call_later_impl(self, delay: typing.Union[int, float],
+                         callback: typing.Callable, *args: typing.Any,
+                         context: typing.Optional[contextvars.Context] = None,
+                         is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
         if not isinstance(delay, (int, float)):
             raise TypeError("delay must be an int or float")
-        return self.call_at(self.time() + delay, callback, *args, context=context)
+        return self._call_at_impl(self.time() + delay, callback, *args, context=context,
+                                  is_threadsafe=is_threadsafe)
 
-    def call_at(self, when: typing.Union[int, float],  # type: ignore[override]
-                callback: typing.Callable, *args: typing.Any,
-                context: typing.Optional[contextvars.Context] = None) -> "QAsyncioHandle":
+    def call_later(self, delay: typing.Union[int, float],
+                   callback: typing.Callable, *args: typing.Any,
+                   context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+        return self._call_later_impl(delay, callback, *args, context=context, is_threadsafe=False)
+
+    def _call_at_impl(self, when: typing.Union[int, float],
+                      callback: typing.Callable, *args: typing.Any,
+                      context: typing.Optional[contextvars.Context] = None,
+                      is_threadsafe: typing.Optional[bool] = False) -> asyncio.TimerHandle:
         if not isinstance(when, (int, float)):
             raise TypeError("when must be an int or float")
         if self.is_closed():
             raise RuntimeError("Event loop is closed")
-        return QAsyncioTimerHandle(when, callback, args, self, context)
+        return QAsyncioTimerHandle(when, callback, args, self, context, is_threadsafe=is_threadsafe)
+
+    def call_at(self, when: typing.Union[int, float],
+                callback: typing.Callable, *args: typing.Any,
+                context: typing.Optional[contextvars.Context] = None) -> asyncio.TimerHandle:
+        return self._call_at_impl(when, callback, *args, context=context, is_threadsafe=False)
 
     def time(self) -> float:
         return QDateTime.currentMSecsSinceEpoch() / 1000
@@ -459,7 +507,8 @@ class QAsyncioEventLoop(asyncio.BaseEventLoop, QObject):
 
     def default_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
         # TODO
-        print(context["message"])
+        if context["message"]:
+            print(context["message"])
 
     def call_exception_handler(self, context: typing.Dict[str, typing.Any]) -> None:
         if self._exception_handler is not None:
@@ -499,11 +548,13 @@ class QAsyncioHandle():
         DONE = enum.auto()
 
     def __init__(self, callback: typing.Callable, args: typing.Tuple,
-                 loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context]) -> None:
+                 loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
+                 is_threadsafe: typing.Optional[bool] = False) -> None:
         self._callback = callback
         self._args = args
         self._loop = loop
         self._context = context
+        self._is_threadsafe = is_threadsafe
 
         self._timeout = 0
 
@@ -512,7 +563,10 @@ class QAsyncioHandle():
 
     def _schedule_event(self, timeout: int, func: typing.Callable) -> None:
         if not self._loop.is_closed() and not self._loop._quit_from_outside:
-            QTimer.singleShot(timeout, self._loop, func)
+            if self._is_threadsafe:
+                QTimer.singleShot(timeout, self._loop, func)
+            else:
+                QTimer.singleShot(timeout, func)
 
     def _start(self) -> None:
         self._schedule_event(self._timeout, lambda: self._cb())
@@ -535,15 +589,16 @@ class QAsyncioHandle():
         return self._state == QAsyncioHandle.HandleState.CANCELLED
 
 
-class QAsyncioTimerHandle(QAsyncioHandle):
+class QAsyncioTimerHandle(QAsyncioHandle, asyncio.TimerHandle):
     def __init__(self, when: float, callback: typing.Callable, args: typing.Tuple,
-                 loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context]) -> None:
-        super().__init__(callback, args, loop, context)
+                 loop: QAsyncioEventLoop, context: typing.Optional[contextvars.Context],
+                 is_threadsafe: typing.Optional[bool] = False) -> None:
+        QAsyncioHandle.__init__(self, callback, args, loop, context, is_threadsafe)
 
         self._when = when
         self._timeout = int(max(self._when - self._loop.time(), 0) * 1000)
 
-        super()._start()
+        QAsyncioHandle._start(self)
 
     # Override this so that timer.start() is only called once at the end
     # of the constructor for both QtHandle and QtTimerHandle.
index 7ed8bcb64c88b41b9771e2c3b2956ff285b4e376..611bd5634a7b7401884e962a11da123a408364cb 100644 (file)
@@ -25,15 +25,16 @@ class QAsyncioFuture():
 
     def __init__(self, *, loop: typing.Optional["events.QAsyncioEventLoop"] = None,
                  context: typing.Optional[contextvars.Context] = None) -> None:
+        self._loop: "events.QAsyncioEventLoop"
         if loop is None:
-            self._loop = asyncio.events.get_event_loop()
+            self._loop = asyncio.events.get_event_loop()  # type: ignore[assignment]
         else:
             self._loop = loop
         self._context = context
 
         self._state = QAsyncioFuture.FutureState.PENDING
         self._result: typing.Any = None
-        self._exception: typing.Optional[Exception] = None
+        self._exception: typing.Optional[BaseException] = None
 
         self._callbacks: typing.List[typing.Callable] = list()
 
@@ -44,16 +45,15 @@ class QAsyncioFuture():
             self._asyncio_future_blocking = True
             yield self
             if not self.done():
-                raise RuntimeError("await was not used with a Future")
+                raise RuntimeError("await was not used with a Future or Future-like object")
         return self.result()
 
     __iter__ = __await__
 
     def _schedule_callbacks(self, context: typing.Optional[contextvars.Context] = None):
-        if self._loop.is_running():
-            for cb in self._callbacks:
-                self._loop.call_soon(
-                    cb, self, context=context if context else self._context)
+        for cb in self._callbacks:
+            self._loop.call_soon(
+                cb, self, context=context if context else self._context)
 
     def result(self) -> typing.Union[typing.Any, Exception]:
         if self._state == QAsyncioFuture.FutureState.DONE_WITH_RESULT:
@@ -96,14 +96,15 @@ class QAsyncioFuture():
         self._callbacks = [_cb for _cb in self._callbacks if _cb != cb]
         return original_len - len(self._callbacks)
 
-    def cancel(self) -> bool:
+    def cancel(self, msg: typing.Optional[str] = None) -> bool:
         if self.done():
             return False
         self._state = QAsyncioFuture.FutureState.CANCELLED
+        self._cancel_message = msg
         self._schedule_callbacks()
         return True
 
-    def exception(self) -> typing.Optional[Exception]:
+    def exception(self) -> typing.Optional[BaseException]:
         if self._state == QAsyncioFuture.FutureState.CANCELLED:
             raise asyncio.CancelledError
         if self.done():
index 78f9dfb0c039edad5ee5532dbc38157c61a7247d..bc3d41a736ccdcdfc16d9ca6c37e1b816cfdd2e6 100644 (file)
@@ -27,6 +27,9 @@ class QAsyncioTask(futures.QAsyncioFuture):
 
         self._cancellation_requests = 0
 
+        self._future_to_await: typing.Optional[asyncio.Future] = None
+        self._cancel_message: typing.Optional[str] = None
+
         asyncio._register_task(self)  # type: ignore[arg-type]
 
     def __repr__(self) -> str:
@@ -44,14 +47,6 @@ class QAsyncioTask(futures.QAsyncioFuture):
     class QtTaskApiMisuseError(Exception):
         pass
 
-    def __await__(self) -> None:  # type: ignore[override]
-        # This function is not inherited from the Future APIs.
-        raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot be awaited")
-
-    def __iter__(self) -> None:  # type: ignore[override]
-        # This function is not inherited from the Future APIs.
-        raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot be iterated over")
-
     def set_result(self, result: typing.Any) -> None:  # type: ignore[override]
         # This function is not inherited from the Future APIs.
         raise QAsyncioTask.QtTaskApiMisuseError("Tasks cannot set results")
@@ -66,6 +61,7 @@ class QAsyncioTask(futures.QAsyncioFuture):
         if self.done():
             return
         result = None
+        self._future_to_await = None
 
         try:
             asyncio._enter_task(self._loop, self)  # type: ignore[arg-type]
@@ -83,16 +79,17 @@ class QAsyncioTask(futures.QAsyncioFuture):
         except StopIteration as e:
             self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_RESULT
             self._result = e.value
-        except concurrent.futures.CancelledError as e:
+        except (concurrent.futures.CancelledError, asyncio.exceptions.CancelledError) as e:
             self._state = futures.QAsyncioFuture.FutureState.CANCELLED
             self._exception = e
         except BaseException as e:
             self._state = futures.QAsyncioFuture.FutureState.DONE_WITH_EXCEPTION
-            self._exception = e  # type: ignore[assignment]
+            self._exception = e
         else:
             if asyncio.futures.isfuture(result):
                 result.add_done_callback(
                     self._step, context=self._context)  # type: ignore[arg-type]
+                self._future_to_await = result
             elif result is None:
                 self._loop.call_soon(self._step, context=self._context)
             else:
@@ -137,7 +134,8 @@ class QAsyncioTask(futures.QAsyncioFuture):
             return False
         self._cancel_message = msg
         self._handle.cancel()
-        self._state = futures.QAsyncioFuture.FutureState.CANCELLED
+        if self._future_to_await is not None:
+            self._future_to_await.cancel(msg)
         return True
 
     def uncancel(self) -> None:
index 5168991f3b1a04d99187bf4079091cdc07674227..f7a4d3cc6dcdfcd6ff198c79a63fb5bd6ee8cc73 100644 (file)
@@ -25,6 +25,7 @@ ${QtCharts_GEN_DIR}/qcandlestickset_wrapper.cpp
 ${QtCharts_GEN_DIR}/qcategoryaxis_wrapper.cpp
 ${QtCharts_GEN_DIR}/qchart_wrapper.cpp
 ${QtCharts_GEN_DIR}/qchartview_wrapper.cpp
+${QtCharts_GEN_DIR}/qcoloraxis_wrapper.cpp
 ${QtCharts_GEN_DIR}/qdatetimeaxis_wrapper.cpp
 ${QtCharts_GEN_DIR}/qhbarmodelmapper_wrapper.cpp
 ${QtCharts_GEN_DIR}/qhboxplotmodelmapper_wrapper.cpp
index f271b77673889ec137c0df742b3ab599c9c02ea7..a9cd73173330d23191367fa067bda48fef29d0af 100644 (file)
           </modify-argument>
       </modify-function>
   </object-type>
+  <object-type name="QColorAxis"/>
   <object-type name="QDateTimeAxis"/>
   <object-type name="QHBarModelMapper"/>
   <object-type name="QHBoxPlotModelMapper"/>
index 2011bd3e76540d01aed1c4e0e0b85628b51944f2..5168373fd650c0bde85b49af79d3faad1e6294d5 100644 (file)
   <rejection class="*" field-name="d_ptr"/>
   <rejection class="QMetaMethod" enum-name="Attributes"/>
   <rejection class="QMetaMethod" field-name="data"/>
+  <!-- Note: Default parameter values of Disambiguated_t as defined by
+       QT6_DECL_NEW_OVERLOAD_TAIL are not seen by the clang parser since it
+       is relying on code snippets for the values. -->
   <rejection class="Qt" field-name="Disambiguated"/>
   <rejection class="" enum-name="QCborNegativeInteger"/>
 
       <modify-argument index="1" pyi-type="Optional[PySide6.QtCore.QObject]"/>
     </modify-function>
     <modify-function signature="connect(const QObject*,const char*,const char*,Qt::ConnectionType)const">
-        <modify-argument index="4">
-            <rename to="type"/>
-        </modify-argument>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-1"/>
     </modify-function>
     <!-- static version -->
     <modify-function signature="connect(const QObject*,QMetaMethod,const QObject*,QMetaMethod,Qt::ConnectionType)">
-        <modify-argument index="5">
-            <rename to="type"/>
-        </modify-argument>
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-2"/>
     </modify-function>
     <modify-function signature="connect(const QObject*,const char*,const QObject*,const char*,Qt::ConnectionType)">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-3"/>
     </modify-function>
     <inject-code class="native" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect"/>
-    <add-function signature="connect(const QObject*,const char*,PyCallable*,Qt::ConnectionType@type@=Qt::AutoConnection)"
+    <add-function signature="connect(const QObject*@sender@,const char*@signal@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
                   return-type="QMetaObject::Connection" static="yes">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-4"/>
     </add-function>
+    <add-function signature="connect(const QObject*@sender@,const char*@signal@,const QObject*@context@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
+                  return-type="QMetaObject::Connection" static="yes">
+        <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-4-context"/>
+    </add-function>
     <!-- static version -->
-    <add-function signature="connect(const char*,PyCallable*,Qt::ConnectionType@type@=Qt::AutoConnection)"
+    <add-function signature="connect(const char*@signal@,PyCallable*@functor@,Qt::ConnectionType@type@=Qt::AutoConnection)"
                   return-type="QMetaObject::Connection">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-5"/>
     </add-function>
-    <add-function signature="connect(const char*,const QObject*,const char*,Qt::ConnectionType@type@=Qt::AutoConnection)"
+    <add-function signature="connect(const char*@signal@,const QObject*@receiver@,const char*@method@,Qt::ConnectionType@type@=Qt::AutoConnection)"
                   return-type="QMetaObject::Connection">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-connect-6"/>
     </add-function>
     <add-function signature="emit(const char*,...)" return-type="bool">
         <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-emit"/>
     </add-function>
-    <add-function signature="disconnect(const char*,PyCallable*)" return-type="bool">
+    <add-function signature="disconnect(const char*@signal@,PyCallable*@functor@)" return-type="bool">
          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-disconnect-1"/>
     </add-function>
-    <add-function signature="disconnect(const QObject*,const char*,PyCallable*)" return-type="bool" static="yes">
+    <add-function signature="disconnect(const QObject*@sender@,const char*@signal@,PyCallable*@functor@)" return-type="bool" static="yes">
          <inject-code class="target" position="beginning" file="../glue/qtcore.cpp" snippet="qobject-disconnect-2"/>
     </add-function>
+    <modify-function signature="disconnect(QMetaObject::Connection)">
+        <modify-argument index="1">
+            <rename to="connection"/>
+        </modify-argument>
+    </modify-function>
 
 
     <add-function signature="findChild(PyTypeObject*@type@,const QString&amp;@name@={},Qt::FindChildOptions@options@=Qt::FindChildrenRecursively)"
   <suppress-warning text="skipping field 'State::clearFn' with unmatched type 'void'"/>
   <suppress-warning text="template baseclass 'QListSpecialMethods&lt;T&gt;' of 'QList' is not known"/>
   <suppress-warning text="^.*inherits from a non polymorphic type.*QIODeviceBase.*type discovery based on RTTI is impossible.*$"/>
+  <suppress-warning text="Base class 'QOperatingSystemVersionUnexported' of class 'QOperatingSystemVersion' not found in the type system for setting up inheritance."/>
 
 </typesystem>
index 8580c339e2bd483dd841c71ecd8d73b63b0e13be..02b26d7e562bcb5de216240733a5746246dca9e4 100644 (file)
@@ -53,6 +53,8 @@
           <define-ownership class="target" owner="default"/>
         </modify-argument>
       </modify-function>
+      <!-- PYSIDE-2547, hangs -->
+      <modify-function signature="^connect\(.*\)$" allow-thread="yes"/>
     </value-type>
     <object-type name="QDBusConnectionInterface">
       <enum-type name="RegisterServiceReply"/>
     </object-type>
     <object-type name="QDBusSignature"/>
     <object-type name="QDBusUnixFileDescriptor"/>
-    <object-type name="QDBusVariant"/>
+    <value-type name="QDBusVariant"/>
     <object-type name="QDBusVirtualObject"/>
     <suppress-warning text='^.*Unable to translate type "QDBusReply&lt;.*$'/>
     <suppress-warning text='^.*QDBusPendingCallWatcher inherits from a non polymorphic type.*$'/>
index 0f5fcd479178d2e6e92da168988fb754f6c307e1..da150af76f98b21349bf190616cd904bb50e37a3 100644 (file)
@@ -11,6 +11,7 @@ set(QtGui_DROPPED_ENTRIES)
 set(QtGui_SRC_UNITY_EXCLUDED_SRC
     ${QtGui_GEN_DIR}/qtextframe_iterator_wrapper.cpp
     ${QtGui_GEN_DIR}/qtextblock_iterator_wrapper.cpp
+    ${QtGui_GEN_DIR}/qshaderversion_wrapper.cpp
 )
 
 set_property(SOURCE ${QtGui_SRC_UNITY_EXCLUDED_SRC}
@@ -64,7 +65,6 @@ ${QtGui_GEN_DIR}/qrhiviewport_wrapper.cpp
 ${QtGui_GEN_DIR}/qshader_wrapper.cpp
 ${QtGui_GEN_DIR}/qshadercode_wrapper.cpp
 ${QtGui_GEN_DIR}/qshaderkey_wrapper.cpp
-${QtGui_GEN_DIR}/qshaderversion_wrapper.cpp
 )
 
 if (ENABLE_WIN)
index b77ab0b34fcd5d83be39a1eb41d899faffa48e4a..022a5ab0948c6723ef7e131414e04dd243133c37 100644 (file)
         <include file-name="QSize" location="global"/>
     </extra-includes>
     <!-- FIXME: PYSIDE7: Handle setQuery(QSqlQuery&&) in some way?
-         QTBUG-91766/PYSIDE-2394 -->
+         QTBUG-91766/PYSIDE-2394. allow-thread for PYSIDE-1931 -->
     <modify-function signature="setQuery(QSqlQuery)" allow-thread="yes"
                      deprecated="false"/>
     <modify-function signature="setQuery(QString,QSqlDatabase)" allow-thread="yes"/>
         <include file-name="QStringList" location="global"/>
         <include file-name="QSize" location="global"/>
     </extra-includes>
+    <modify-function signature="select()" allow-thread="yes"/> <!-- PYSIDE-1931 -->
   </object-type>
   <object-type name="QSqlResult">
       <enum-type name="BindingSyntax"/>
         <include file-name="QStringList" location="global"/>
         <include file-name="QSize" location="global"/>
     </extra-includes>
+    <modify-function signature="select()" allow-thread="yes"/> <!-- PYSIDE-1931 -->
   </object-type>
   <object-type name="QSqlDriverCreatorBase">
     <extra-includes>
index 52a07aa2686a141dd2e367ad35e226e720a1e4fb..94dc915a09a3d1fec4e9230e4fcd61fe06e616ef 100644 (file)
@@ -321,6 +321,12 @@ PyModule_AddStringConstant(module, "__version__", qVersion());
 %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
 // @snippet qobject-connect-4
 
+// @snippet qobject-connect-4-context
+// %FUNCTION_NAME() - disable generation of function call.
+%RETURN_TYPE %0 = PySide::qobjectConnectCallback(%1, %2, %3, %PYARG_4, %5);
+%PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
+// @snippet qobject-connect-4-context
+
 // @snippet qobject-connect-5
 // %FUNCTION_NAME() - disable generation of function call.
 %RETURN_TYPE %0 = PySide::qobjectConnectCallback(%CPPSELF, %1, %PYARG_2, %3);
@@ -430,8 +436,7 @@ if (%PYARG_0 == Py_None)
 namespace PySide {
     template<> inline Py_ssize_t hash(const QLine &l)
     {
-        const int v[4] = {l.x1(), l.y1(), l.x2(), l.y2()};
-        return qHashRange(v, v + 4);
+        return qHashMulti(0, l.x1(), l.y1(), l.x2(), l.y2());
     }
 };
 // @snippet qline-hash
@@ -1054,28 +1059,9 @@ timer->start(%1);
 // @snippet qtimer-singleshot-2
 
 // @snippet qtimer-singleshot-functor-context
-Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
-if (PyObject_TypeCheck(%3, PySideSignalInstance_TypeF())) {
-    auto *timerType = Shiboken::SbkType<QTimer>();
-    auto *pyTimer = timerType->tp_new(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
-    timerType->tp_init(pyTimer, emptyTuple, nullptr);
-    QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
-    timer->setSingleShot(true);
-    PySideSignalInstance *signalInstance = reinterpret_cast<PySideSignalInstance *>(%2);
-    Shiboken::AutoDecRef signalSignature(Shiboken::String::fromFormat("2%s", PySide::Signal::getSignature(signalInstance)));
-    Shiboken::AutoDecRef result(
-        PyObject_CallFunction(PySide::PySideName::qtConnect(), "OsOO",
-                              pyTimer,
-                              SIGNAL(timeout()),
-                              %3,
-                              PySide::Signal::getObject(signalInstance),
-                              signalSignature.object())
-    );
-    timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
-    Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
-    Py_XDECREF(pyTimer);
-    timer->start(%1);
-} else {
+auto msec = %1;
+if (msec == 0) {
+    Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
     auto *callable = %PYARG_3;
     auto cppCallback = [callable]()
     {
@@ -1086,7 +1072,30 @@ if (PyObject_TypeCheck(%3, PySideSignalInstance_TypeF())) {
     };
 
     Py_INCREF(callable);
-    %CPPSELF.%FUNCTION_NAME(%1, %2, cppCallback);
+    %CPPSELF.%FUNCTION_NAME(msec, %2, cppCallback);
+} else {
+    Shiboken::AutoDecRef emptyTuple(PyTuple_New(0));
+    auto *timerType = Shiboken::SbkType<QTimer>();
+    auto newFunc = timerType->tp_new;
+    auto initFunc = timerType->tp_init;
+    auto *pyTimer = newFunc(Shiboken::SbkType<QTimer>(), emptyTuple, nullptr);
+    initFunc(pyTimer, emptyTuple, nullptr);
+
+    QTimer * timer = %CONVERTTOCPP[QTimer *](pyTimer);
+    timer->setSingleShot(true);
+
+    Shiboken::AutoDecRef result(
+        PyObject_CallMethod(pyTimer, "connect", "OsOO",
+                            pyTimer,
+                            SIGNAL(timeout()),
+                            %PYARG_2,
+                            %PYARG_3)
+    );
+
+    timer->connect(timer, &QTimer::timeout, timer, &QObject::deleteLater, Qt::DirectConnection);
+    Shiboken::Object::releaseOwnership(reinterpret_cast<SbkObject *>(pyTimer));
+    Py_XDECREF(pyTimer);
+    timer->start(msec);
 }
 // @snippet qtimer-singleshot-functor-context
 
@@ -1440,7 +1449,7 @@ auto res = (*%CPPSELF) + cppArg0;
 
 // @snippet conversion-pystring-char
 char c = %CONVERTTOCPP[char](%in);
-%out = %OUTTYPE(c);
+%out = %OUTTYPE(static_cast<unsigned short>(c));
 // @snippet conversion-pystring-char
 
 // @snippet conversion-pyint
@@ -1921,7 +1930,7 @@ auto callback = [callable]() -> void
         return;
     }
     Shiboken::GilState state;
-    PyObject_CallObject(callable, nullptr);
+    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, nullptr));
     Py_DECREF(callable);
 };
 Py_INCREF(callable);
@@ -2008,9 +2017,9 @@ auto callback = [callable, count, arg_qpermission](const QPermission &permission
     if (arg_qpermission) {
         Shiboken::AutoDecRef arglist(PyTuple_New(1));
         PyTuple_SET_ITEM(arglist.object(), 0, %CONVERTTOPYTHON[QPermission](permission));
-        PyObject_CallObject(callable, arglist);
+        Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     } else {
-        PyObject_CallObject(callable, nullptr);
+        Shiboken::AutoDecRef ret(PyObject_CallObject(callable, nullptr));
     }
     Py_DECREF(callable);
 };
index e22569e2ee839c669b2ee8a996deb0c280b07f73..f390605d2a8545cfa78346ec4a0a2195664a27fd 100644 (file)
@@ -16,13 +16,15 @@ auto callback = [callable](QAbstractOAuth::Stage stage, QMultiMap<QString, QVari
     PyTuple_SET_ITEM(arglist, 1, %CONVERTTOPYTHON[QMultiMap<QString, QVariant>](dict));
     Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
 
-    PyObject *key;
-    PyObject *value;
-    Py_ssize_t pos = 0;
-    while (PyDict_Next(ret, &pos, &key, &value)) {
-        QString cppKey = %CONVERTTOCPP[QString](key);
-        QVariant cppValue = %CONVERTTOCPP[QVariant](value);
-        dictPointer->replace(cppKey, cppValue);
+    if (!ret.isNull() && PyDict_Check(ret.object()) != 0) {
+        PyObject *key{};
+        PyObject *value{};
+        Py_ssize_t pos = 0;
+        while (PyDict_Next(ret.object(), &pos, &key, &value)) {
+            QString cppKey = %CONVERTTOCPP[QString](key);
+            QVariant cppValue = %CONVERTTOCPP[QVariant](value);
+            dictPointer->replace(cppKey, cppValue);
+        }
     }
 
     Py_DECREF(callable);
index 9e52436e7424372c3988f65812b29ca70dac9436..c87cf9e7bcd580c77d2e25d654b389129811d590 100644 (file)
@@ -62,7 +62,7 @@ Q_IMPORT_PLUGIN(PyCustomWidgets);
 
 // @snippet quiloader-registercustomwidget
 registerCustomWidget(%PYARG_1);
-%CPPSELF.addPluginPath(""); // force reload widgets
+%CPPSELF.addPluginPath(QString{}); // force reload widgets
 // @snippet quiloader-registercustomwidget
 
 // @snippet quiloader-load-1
@@ -87,7 +87,7 @@ char *arg1 = PyBytes_AsString(strObj);
 QByteArray uiFileName(arg1);
 Py_DECREF(strObj);
 
-QFile uiFile(uiFileName);
+QFile uiFile(QString::fromUtf8(uiFileName));
 
 if (!uiFile.exists()) {
     qCritical().noquote() << "File" << uiFileName << "does not exists";
@@ -102,7 +102,7 @@ if (uiFileName.isEmpty()) {
 // Use the 'pyside6-uic' wrapper instead of 'uic'
 // This approach is better than rely on 'uic' since installing
 // the wheels cover this case.
-QString uicBin("pyside6-uic");
+QString uicBin(QStringLiteral("pyside6-uic"));
 QStringList uicArgs = {QString::fromUtf8(uiFileName)};
 
 QProcess uicProcess;
index a569e6c11ac4e209788905cb58770f5a19c972b6..0a0a00ec8231798607de0f3b1eb3d5cbe802b15e 100644 (file)
@@ -10,11 +10,11 @@ auto callback = [callable](const QWebEngineCookieStore::FilterRequest& filterReq
     PyTuple_SET_ITEM(arglist, 0,
                     %CONVERTTOPYTHON[QWebEngineCookieStore::FilterRequest](filterRequest));
     Py_INCREF(callable);
-    PyObject* ret = PyObject_CallObject(callable, arglist);
+    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     Py_DECREF(callable);
-    return ret;
-
+    return ret.object() == Py_True;
 };
+
 %CPPSELF.%FUNCTION_NAME(callback);
 // @snippet qwebenginecookiestore-setcookiefilter
 
@@ -24,13 +24,13 @@ auto callback = [callable](std::unique_ptr<QWebEngineNotification> webEngineNoti
 {
     Shiboken::GilState state;
     Shiboken::AutoDecRef arglist(PyTuple_New(1));
+    auto *notification = webEngineNotification.release();
     PyTuple_SET_ITEM(arglist.object(), 0,
-                    Shiboken::Conversions::pointerToPython(
-                        SbkPySide6_QtWebEngineCoreTypes[SBK_QWEBENGINENOTIFICATION_IDX],
-                        webEngineNotification.release()));
+                     %CONVERTTOPYTHON[QWebEngineNotification*](notification));
     Py_INCREF(callable);
-    PyObject_CallObject(callable, arglist);
+    Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     Py_DECREF(callable);
 };
+
 %CPPSELF.%FUNCTION_NAME(callback);
 // @snippet qwebengineprofile-setnotificationpresenter
index 8fdd6b69337917bb773af2af67c6709c1fc11bc7..af15130a49c6a35f2a767ed33c4f4e95e3fb710f 100644 (file)
@@ -55,8 +55,8 @@ auto callback = [callable](const QString &text)
     PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QString](text));
     Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     Py_DECREF(callable);
-
 };
+
 Py_INCREF(callable);
 %CPPSELF.%FUNCTION_NAME(callback);
 // @snippet qwebenginepage-convertto
@@ -95,8 +95,8 @@ auto callback = [callable](const QVariant &result)
    // PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[bool](found));
     Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     Py_DECREF(callable);
-
 };
+
 Py_INCREF(callable);
 %CPPSELF.%FUNCTION_NAME(%1, %2, callback);
 // @snippet qwebenginepage-runjavascript
@@ -114,8 +114,8 @@ auto callback = [callable](const QByteArray &pdf)
     PyTuple_SET_ITEM(arglist, 0, %CONVERTTOPYTHON[QByteArray](pdf));
     Shiboken::AutoDecRef ret(PyObject_CallObject(callable, arglist));
     Py_DECREF(callable);
-
 };
+
 Py_INCREF(callable);
 %CPPSELF.%FUNCTION_NAME(callback, %2);
 // @snippet qwebenginepage-printtopdf
index c059a2a3d14c0848706664b8f3afc26dbc061f58..6a7404507ff2b9fdfcfb4003aed8290a538decd6 100644 (file)
@@ -8,7 +8,7 @@
        int size = Shiboken::String::len(%PYARG_1);
        if (size == 1) {
            const char *str = Shiboken::String::toCString(%PYARG_1);
-           QChar ch(str[0]);
+           const QChar ch(static_cast&lt;unsigned short&gt;(str[0]));
            %RETURN_TYPE %0 = %CPPSELF.%FUNCTION_NAME(ch);
            %PYARG_0 = %CONVERTTOPYTHON[%RETURN_TYPE](%0);
        } else {
index a54f529f9efa8eca42845bd1fd72abaf1e2c99b1..bf6054a76436bccec166b0a35d4e4ff24a4bf5cd 100644 (file)
@@ -280,7 +280,7 @@ macro(create_pyside_module)
     # Don't generate the files when cross-building because the target python can not be executed
     # on the host machine (usually, unless you use some userspace qemu based mechanism).
     # TODO: Can we do something better here to still get pyi files?
-    if(NOT PYSIDE_IS_CROSS_BUILD)
+    if(NOT (PYSIDE_IS_CROSS_BUILD OR DISABLE_PYI))
         set(generate_pyi_options ${module_NAME} --sys-path
             "${pysidebindings_BINARY_DIR}"
             "${SHIBOKEN_PYTHON_MODULE_DIR}/..")     # use the layer above shiboken6
index 87a91cfd94e16e302f30c4c050d480a30468dd25..038dc164252ca1946e8d272900942c815be28436 100644 (file)
@@ -43,6 +43,10 @@ pyside_internal_set_up_extra_dependency_paths()
 pyside_internal_find_host_shiboken_tools()
 find_package(Shiboken6 6 CONFIG REQUIRED)
 
+if(is_pyside6_superproject_build)
+    shiboken_find_required_python()
+endif()
+
 set(BINDING_API_MAJOR_VERSION "${pyside_MAJOR_VERSION}")
 set(BINDING_API_MINOR_VERSION "${pyside_MINOR_VERSION}")
 set(BINDING_API_MICRO_VERSION "${pyside_MICRO_VERSION}")
@@ -51,7 +55,7 @@ set(BINDING_API_PRE_RELEASE_VERSION "${pyside_PRE_RELEASE_VERSION}")
 
 # Detect if the Python interpreter is actually PyPy
 execute_process(
-    COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+    COMMAND ${Python_EXECUTABLE} -c "if True:
         pypy_version = ''
         import sys
         if hasattr(sys, 'pypy_version_info'):
index ee37f8aa1d049c28ba038e650decfdddead0273b..bf55263ffd532edb0f6672416bd7a9c07f35501a 100644 (file)
@@ -63,7 +63,6 @@ file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/pyside.qdocconf ${CMAKE_CURRENT_LIST_DIR}/
 # for a rst_build_docs case, and not a full doc build
 if (NOT FULLDOCSBUILD)
     find_package(Python COMPONENTS Interpreter)
-    set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
 endif()
 
 if (QT_SRC_DIR)
@@ -75,7 +74,7 @@ endif()
 if(PYSIDE_IS_CROSS_BUILD)
     set(python_executable "${QFP_PYTHON_HOST_PATH}")
 else()
-    set(python_executable "${PYTHON_EXECUTABLE}")
+    set(python_executable "${Python_EXECUTABLE}")
 endif()
 
 set(TOOLS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../tools")
@@ -134,7 +133,7 @@ if (FULLDOCSBUILD)
     file(WRITE ${global_typesystem} "${typeSystemDocXmlContents}")
 
     execute_process(
-      COMMAND ${PYTHON_EXECUTABLE} "${TOOLS_DIR}/doc_modules.py"
+      COMMAND ${Python_EXECUTABLE} "${TOOLS_DIR}/doc_modules.py"
               -t "${global_typesystem}" -g "${global_header}" -d "${config_docconf}"
               "${QT_INCLUDE_DIR}" "${SUPPORTED_QT_VERSION}"
       OUTPUT_VARIABLE ALL_DOC_MODULES
@@ -158,6 +157,7 @@ if (FULLDOCSBUILD)
     file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/${qtdoc_doc_conf}\n")
 
     configure_file("pyside.qdocconf.in" "pyside.qdocconf" @ONLY)
+    configure_file("qdoc_spawner.py.in" "qdoc_spawner.py" @ONLY)
 
 
     set(QDOC_TYPESYSTEM_PATH "${pyside6_SOURCE_DIR}${PATH_SEP}${pyside6_BINARY_DIR}")
@@ -172,20 +172,25 @@ if (FULLDOCSBUILD)
     add_custom_target(qdoc DEPENDS "${DOC_DATA_DIR}/webxml/qtcore-index.webxml")
 
     add_custom_command(OUTPUT "${DOC_DATA_DIR}/webxml/qtcore-index.webxml"
-                       # Use dummy Qt version information, QDoc needs it but has no effect on WebXML output
-                       COMMAND ${CMAKE_COMMAND} -E env BUILDDIR=${CMAKE_CURRENT_LIST_DIR}/src QT_INSTALL_DOCS=${QT_SRC_DIR}/doc
-                       QT_VERSION=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}
-                       QT_VER=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}
-                       QT_VERSION_TAG=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}
-                       "${qdoc_binary}" pyside.qdocconf -single-exec
-                           -installdir ${DOC_DATA_DIR} -outputdir ${DOC_DATA_DIR}
+                        Use dummy Qt version information, QDoc needs it but has no effect on WebXML output
+                       COMMAND ${CMAKE_COMMAND} -E env ${python_executable} qdoc_spawner.py
+                           --qt=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}
+                           --doc-data-dir=${DOC_DATA_DIR}
+                           --qdoc-binary=${qdoc_binary}
+                           --build-dir=${CMAKE_CURRENT_LIST_DIR}/src
+                           --qt-install-docs=${QT_SRC_DIR}/doc
+                           --parallel="auto"
+                           --verbose
                        COMMENT "Running qdoc against Qt source code...")
 endif()
 
+# Avoid using 'auto' for '-j' option as it could lead to
+# crash on some systems due to out-of-memory situation. Instead
+# use a no. less than 8.
 add_custom_target(apidoc
                   COMMAND ${CMAKE_COMMAND} -E env INHERITANCE_FILE=${ENV_INHERITANCE_FILE}
                           ${SHIBOKEN_PYTHON_INTERPRETER} ${SPHINX_BUILD} -b ${DOC_OUTPUT_FORMAT}
-                          -j auto ${CMAKE_CURRENT_BINARY_DIR}/rst html
+                          -j 6 ${CMAKE_CURRENT_BINARY_DIR}/rst html
                   COMMENT "Generating PySide htmls..."
                  )
 
@@ -198,6 +203,7 @@ if(DOC_OUTPUT_FORMAT STREQUAL "html")
                     ${CMAKE_CURRENT_BINARY_DIR}/../../shiboken6/doc/html
                     ${CMAKE_CURRENT_BINARY_DIR}/html/shiboken6
             COMMENT "Copying Shiboken docs..."
+            DEPENDS "${DOC_DATA_DIR}/webxml/qtcore-index.webxml"
             VERBATIM)
 else()
     if(qhelpgenerator_binary)
@@ -209,6 +215,7 @@ else()
                 COMMAND ${python_executable} ${PATCH_QHP_SCRIPT} -p -v pyside6 ${QHP_FILE}
                 COMMAND "${qhelpgenerator_binary}" ${QHP_FILE}
                 COMMENT "Generating QCH from a QHP file..."
+                DEPENDS "${DOC_DATA_DIR}/webxml/qtcore-index.webxml"
                 VERBATIM
         )
     else()
diff --git a/sources/pyside6/doc/PySide6/QtAsyncio/index.rst b/sources/pyside6/doc/PySide6/QtAsyncio/index.rst
new file mode 100644 (file)
index 0000000..90c0ee6
--- /dev/null
@@ -0,0 +1,145 @@
+.. module:: PySide6.QtAsyncio
+
+PySide6.QtAsyncio
+*****************
+
+.. note:: This module is currently in technical preview.
+
+The Qt Asyncio module is a pure Python module that allows programs to be
+written that use Qt's API in conjunction with `asyncio
+<https://docs.python.org/3/library/asyncio.html>`_. asyncio is a popular
+Python library for asynchronous programming. It is used in particular
+for programs that need to handle many I/O operations from many sources,
+such as web servers. More generally, it allows developers to work with
+`couroutines <https://docs.python.org/3/library/asyncio-task.html#coroutine>`_.
+Coroutines can be imagined as "asynchronous functions". In contrast to
+Qt's signals and slot mechanism, this allows for asynchronous programs
+that are closer in program flow to synchronous programs, as programs no
+longer have to be imagined as a series of callbacks. Instead, coroutines
+transparently resume and yield at designated spots.
+
+Consider the following simple coroutine defined with the ``async``
+keyword in front of its definition:
+
+::
+
+    async def do_something():
+        result = await do_something_asynchronously()
+        print(result)
+
+``do_something_asynchronously()`` is a coroutine itself, e.g., an
+I/O-heavy operation that would normally block the execution flow in a
+synchronous program. Instead, the ``await`` keyword is used to wait for
+the result, at which point ``do_something()`` yields and the program
+flow transparently switches to the next asynchronous task. When the
+result becomes available, the program flow is able to switch back to the
+``do_something()`` coroutine, which then resumes and prints the result.
+
+The asyncio API
+^^^^^^^^^^^^^^^
+
+asyncio and Qt are both based on an event loop. asyncio provides an API
+to replace its default event loop with a custom implementation.
+**QtAsyncio** provides such an implementation that uses Qt's event loop,
+allowing Qt and asyncio to be used together.
+
+We consider that this API consists of two levels:
+
+1.  Fundamental infrastructure for event loops and asynchronous
+    operations, including `futures
+    <https://docs.python.org/3/library/asyncio-future.html#asyncio.Future>`_,
+    `tasks <https://docs.python.org/3/library/asyncio-task.html#asyncio.Task>`_,
+    `handles <https://docs.python.org/3/library/asyncio-eventloop.html#callback-handles>`_,
+    executors, and event loop management functions (see below).
+2.  A user-facing API for use in applications, including transports and
+    protocols, network connections, servers, sockets, signals,
+    subprocesses.
+
+**QtAsyncio** currently covers the first level. This includes the
+following functions, for which the API is identical with QtAsyncio as
+with asyncio:
+
+* `run_until_complete() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_until_complete>`_
+* `run_forever() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_forever>`_
+* `stop() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.stop>`_
+* `is_running() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.is_running>`_
+* `is_closed() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.is_closed>`_
+* `close() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.close>`_
+* `shutdown_asyncgens() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_asyncgens>`_
+* `shutdown_default_executor() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.shutdown_default_executor>`_
+* `call_soon() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon>`_
+* `call_soon_threadsafe() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon_threadsafe>`_
+* `call_later() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_later>`_
+* `call_at() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_at>`_
+* `time() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.time>`_
+* `create_future() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_future>`_
+* `create_task() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.create_task>`_
+* `set_task_factory() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.set_task_factory>`_
+* `get_task_factory() <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.get_task_factory>`_
+
+Also included is the ability to
+`run synchronous code in an executor <https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor>`_
+(``ThreadPoolExecutor``).
+
+Get started with QtAsyncio
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To write a program with **QtAsyncio**, first import the module, e.g.:
+
+::
+
+    import PySide6.QtAsyncio as QtAsyncio
+
+**QtAsyncio** provides a function ``run()`` that can be used to run a
+specific coroutine until it is complete, or to start the Qt & asyncio
+event loop plainly. The former case makes sense if the program flow
+starts with said coroutine, the latter case makes sense if a coroutine
+is enqueued later in the program flow, e.g., after pressing a button in
+the UI.
+
+::
+
+    QtAsyncio.run()
+
+(see the `asyncio "minimal" example <https://doc.qt.io/qtforpython-6/examples/example_async_minimal.html>`_
+for an instance of this usage) or
+
+::
+
+    QtAsyncio.run(my_coroutine())
+
+(see the `asyncio "Eratosthenes" example <https://doc.qt.io/qtforpython-6/examples/example_async_eratosthenes.html>`_)
+or
+
+::
+
+    QtAsyncio.run(my_coroutine(), keep_running=False)
+
+to run the coroutine and then stop the event loop upon its completion.
+This latter case behaves identically to ``asyncio.run(my_coroutine())``.
+
+An additional optional argument ``quit_qapp`` can be passed to ``run()``
+to configure whether the QCoreApplication at the core of QtAsyncio
+should be shut down when asyncio finishes. A special case where one
+would want to disable this is test suites that want to reuse a single
+QCoreApplication instance across all unit tests, which would fail if
+this instance is shut down every time. The default is ``True``.
+
+Note that this argument is orthogonal to the ``keep_running`` argument.
+``keep_running`` determines if **asyncio** should keep running after the
+coroutine has finished, while ``quit_qapp`` determines if the
+QCoreApplication should be shut down after asyncio has finished. It is
+possible for asyncio to finish while the QCoreApplication is kept alive.
+
+Coroutines explained
+^^^^^^^^^^^^^^^^^^^^
+
+Coroutines are functions that can be paused (yield) and resumed. Behind
+this simple concept lies a complex mechanism that is abstracted by the
+asynchronous framework. This talk presents a diagram that attempts to
+illustrate the flow of a coroutine from the moment it's provided to the
+async framework until it's completed.
+
+.. image:: https://img.youtube.com/vi/XuqdTvisqkQ/mqdefault.jpg
+    :alt: Asynchronous programming with asyncio and Qt
+    :target: https://www.youtube.com/watch?v=XuqdTvisqkQ
diff --git a/sources/pyside6/doc/_static/qtforpython.ico b/sources/pyside6/doc/_static/qtforpython.ico
new file mode 100644 (file)
index 0000000..2f4d9e9
Binary files /dev/null and b/sources/pyside6/doc/_static/qtforpython.ico differ
index f9f2350e8af4073f0932c6ac3c0a18319ff07610..81da58370fc3f5a556425252a1453e48af137e8f 100644 (file)
@@ -20,7 +20,6 @@ creating-a-sensor-plugin.webxml
 custom-types.webxml
 database.webxml
 datastreamformat.webxml
-datavisualization-examples.webxml
 dbus-changes-qt6.webxml
 determining-the-default-sensor-for-a-type.webxml
 dialogs.webxml
@@ -117,7 +116,6 @@ qml-tutorial1.webxml
 qml-tutorial2.webxml
 qml-tutorial3.webxml
 qmldiskcache.webxml
-qmlexampletoggleswitch.webxml
 qmlreference.webxml
 qndeffilter-record.webxml
 qoutputrange.webxml
@@ -133,8 +131,6 @@ qsggeometry-texturedpoint2d.webxml
 qsgmaterialshader-graphicspipelinestate.webxml
 qsgmaterialtype.webxml
 qstaticplugin.webxml
-qt3d-advancedcustommaterial-example.webxml
-qt3d-audio-visualizer-qml-example.webxml
 qt3d-basicshapes-cpp-example.webxml
 qt3d-changes-qt6.webxml
 qt3d-cpp.webxml
@@ -142,15 +138,9 @@ qt3d-examples.webxml
 qt3d-multiviewport-example.webxml
 qt3d-overview.webxml
 qt3d-pbr-materials-example.webxml
-qt3d-planets-qml-example.webxml
 qt3d-qml.webxml
-qt3d-scene2d-example.webxml
-qt3d-scene3d-example.webxml
-qt3d-shadow-map-qml-example.webxml
 qt3d-simple-qml-example.webxml
 qt3d-simplecustommaterial-example.webxml
-qt3d-wave-example.webxml
-qt3d-widgets-scene3d-example.webxml
 qt3d-wireframe-example.webxml
 qt3drender-framegraph.webxml
 qt3drender-geometry.webxml
@@ -162,7 +152,6 @@ qtbluetooth-changes-qt6.webxml
 qtbluetooth-le-overview.webxml
 qtbluetooth-lowenergyscanner-example.webxml
 qtbluetooth-overview.webxml
-qtbluetooth-pingpong-example.webxml
 qtcborcommon.webxml
 qtcharts-barmodelmapper-example.webxml
 qtcharts-boxplotchart-example.webxml
@@ -170,7 +159,6 @@ qtcharts-candlestickchart-example.webxml
 qtcharts-changes-qt6.webxml
 qtcharts-customchart-example.webxml
 qtcharts-datetimeaxis-example.webxml
-qtcharts-donutchart-example.webxml
 qtcharts-examples.webxml
 qtcharts-horizontalbarchart-example.webxml
 qtcharts-horizontalpercentbarchart-example.webxml
@@ -179,28 +167,20 @@ qtcharts-legendmarkers-example.webxml
 qtcharts-multiaxis-example.webxml
 qtcharts-openglseries-example.webxml
 qtcharts-overview.webxml
-qtcharts-piechartcustomization-example.webxml
-qtcharts-piechartdrilldown-example.webxml
 qtcharts-pointsselectionandmarkers-example.webxml
 qtcharts-polarchart-example.webxml
 qtcharts-qmlaxes-example.webxml
-qtcharts-qmlchart-example.webxml
 qtcharts-qmlcustomizations-example.webxml
 qtcharts-qmlcustomlegend-example.webxml
 qtcharts-qmlf1legends-example.webxml
 qtcharts-qmloscilloscope-example.webxml
 qtcharts-qmlweather-example.webxml
 qtcharts-scatterchart-example.webxml
-qtcharts-scatterinteractions-example.webxml
 qtcharts-selectedbar-example.webxml
 qtcharts-splinechart-example.webxml
 qtcharts-stackedbarchart-example.webxml
-qtcharts-stackedbarchartdrilldown-example.webxml
 qtcharts-zoomlinechart-example.webxml
 qtconcurrent-imagescaling-example.webxml
-qtconcurrent-map-example.webxml
-qtconcurrent-progressdialog-example.webxml
-qtconcurrent-runfunction-example.webxml
 qtconcurrent-wordcount-example.webxml
 qtconcurrentfilter.webxml
 qtconcurrentmap.webxml
@@ -215,31 +195,6 @@ qtcore-threads-queuedcustomtype-example.webxml
 qtcore-threads-semaphores-example.webxml
 qtcore-threads-waitconditions-example.webxml
 qtcore-tools-contiguouscache-example.webxml
-qtcore-tools-customtype-example.webxml
-qtdatavis3d-custominput-example.webxml
-qtdatavis3d-customitems-example.webxml
-qtdatavis3d-customproxy-example.webxml
-qtdatavis3d-draggableaxes-example.webxml
-qtdatavis3d-itemmodel-example.webxml
-qtdatavis3d-qml3doscilloscope-example.webxml
-qtdatavis3d-qmlaxisdrag-example.webxml
-qtdatavis3d-qmlaxisformatter-example.webxml
-qtdatavis3d-qmlbars-example.webxml
-qtdatavis3d-qmlcustominput-example.webxml
-qtdatavis3d-qmllegend-example.webxml
-qtdatavis3d-qmlmultigraph-example.webxml
-qtdatavis3d-qmlscatter-example.webxml
-qtdatavis3d-qmlspectrogram-example.webxml
-qtdatavis3d-qmlsurface-example.webxml
-qtdatavis3d-qmlsurfacelayers-example.webxml
-qtdatavis3d-rotations-example.webxml
-qtdatavis3d-scatter-example.webxml
-qtdatavis3d-texturesurface-example.webxml
-qtdatavis3d-volumetric-example.webxml
-qtdatavisualization-data-handling.webxml
-qtdatavisualization-interacting-with-data.webxml
-qtdatavisualization-known-issues.webxml
-qtdatavisualization-overview.webxml
 qtdbus-chat-example.webxml
 qtdbus-cmake-qt-add-dbus-adaptor.webxml
 qtdbus-cmake-qt-add-dbus-interface.webxml
@@ -253,8 +208,6 @@ qtdesigner-components.webxml
 qtdesigner-containerextension-example.webxml
 qtdesigner-customwidgetplugin-example.webxml
 qtdesigner-manual.webxml
-qtdesigner-worldtimeclockbuilder-example.webxml
-qtdesigner-worldtimeclockplugin-example.webxml
 qtest-overview.webxml
 qtest-tutorial.webxml
 qtextedit-extraselection.webxml
@@ -271,18 +224,8 @@ qtjavascript.webxml
 qtmath.webxml
 qtmultimedia-apple.webxml
 qtmultimedia-changes-qt6.webxml
-qtmultimedia-multimedia-audiodevices-example.webxml
-qtmultimedia-multimedia-audiorecorder-example.webxml
-qtmultimedia-multimedia-declarative-camera-example.webxml
-qtmultimedia-multimedia-spectrum-example.webxml
-qtmultimedia-multimedia-video-mediaplayer-example.webxml
-qtmultimedia-multimedia-video-qmlvideo-example.webxml
-qtmultimedia-multimedia-video-recorder-example.webxml
-qtmultimedia-multimediawidgets-videographicsitem-example.webxml
-qtmultimedia-multimediawidgets-videowidget-example.webxml
 qtnetwork-broadcastreceiver-example.webxml
 qtnetwork-broadcastsender-example.webxml
-qtnetwork-downloadmanager-example.webxml
 qtnetwork-http-example.webxml
 qtnetwork-multicastreceiver-example.webxml
 qtnetwork-multicastsender-example.webxml
@@ -292,8 +235,6 @@ qtnetwork-securesocketclient-example.webxml
 qtnetwork-secureudpclient-example.webxml
 qtnetwork-secureudpserver-example.webxml
 qtnetwork-torrent-example.webxml
-qtnetworkauth-changes-qt6.webxml
-qtnetworkauth-twittertimeline-example.webxml
 qtnfc-annotatedurl-example.webxml
 qtnfc-changes-qt6.webxml
 qtnfc-ndefeditor-example.webxml
@@ -306,7 +247,6 @@ qtplugin.webxml
 qtpositioning-android.webxml
 qtpositioning-changes-qt6.webxml
 qtpositioning-examples.webxml
-qtpositioning-geoflickr-example.webxml
 qtpositioning-ios.webxml
 qtpositioning-logfilepositionsource-example.webxml
 qtpositioning-plugins.webxml
@@ -327,20 +267,16 @@ qtqml-javascript-imports.webxml
 qtqml-javascript-qmlglobalobject.webxml
 qtqml-javascript-resources.webxml
 qtqml-javascript-topic.webxml
-qtqml-networkaccessmanagerfactory-example.webxml
 qtqml-qml-i18n-example.webxml
-qtqml-qmlextensionplugins-example.webxml
 qtqml-syntax-basics.webxml
 qtqml-syntax-directoryimports.webxml
 qtqml-syntax-imports.webxml
 qtqml-syntax-objectattributes.webxml
 qtqml-syntax-propertybinding.webxml
-qtqml-tool-qmltc.webxml
 qtqml-tutorials-extending-qml-example.webxml
 qtqml-typesystem-basictypes.webxml
 qtqml-typesystem-objecttypes.webxml
 qtqml-typesystem-topic.webxml
-qtqml-xmlhttprequest-example.webxml
 qtquick-animation-example.webxml
 qtquick-bestpractices.webxml
 qtquick-canvas-example.webxml
@@ -350,14 +286,11 @@ qtquick-cppextensionpoints.webxml
 qtquick-customitems-dialcontrol-example.webxml
 qtquick-customitems-flipable-example.webxml
 qtquick-customitems-painteditem-example.webxml
-qtquick-customitems-scrollbar-example.webxml
-qtquick-customitems-tabwidget-example.webxml
 qtquick-draganddrop-example.webxml
 qtquick-effects-particles.webxml
 qtquick-effects-sprites.webxml
 qtquick-effects-topic.webxml
 qtquick-effects-transformations.webxml
-qtquick-externaldraganddrop-example.webxml
 qtquick-imageelements-example.webxml
 qtquick-imageprovider-example.webxml
 qtquick-imageresponseprovider-example.webxml
@@ -385,10 +318,8 @@ qtquick-positioning-righttoleft.webxml
 qtquick-positioning-topic.webxml
 qtquick-quick-accessibility-example.webxml
 qtquick-quickwidgets-quickwidget-example.webxml
-qtquick-righttoleft-example.webxml
 qtquick-scenegraph-custommaterial-example.webxml
 qtquick-scenegraph-d3d11underqml-example.webxml
-qtquick-scenegraph-fboitem-example.webxml
 qtquick-scenegraph-graph-example.webxml
 qtquick-scenegraph-materials.webxml
 qtquick-scenegraph-metaltextureimport-example.webxml
@@ -406,12 +337,8 @@ qtquick-statesanimations-topic.webxml
 qtquick-tableview-gameoflife-example.webxml
 qtquick-tableview-pixelator-example.webxml
 qtquick-text-example.webxml
-qtquick-text-validator.webxml
-qtquick-threading-example.webxml
-qtquick-threading-threadedlistmodel-example.webxml
 qtquick-tool-qmllint.webxml
 qtquick-tools-and-utilities.webxml
-qtquick-touchinteraction-example.webxml
 qtquick-tutorials-dynamicview-dynamicview1-example.webxml
 qtquick-tutorials-dynamicview-dynamicview2-example.webxml
 qtquick-tutorials-dynamicview-dynamicview3-example.webxml
@@ -436,38 +363,35 @@ qtquickcontrols-contactlist-example.webxml
 qtquickcontrols-eventcalendar-example.webxml
 qtquickcontrols-flatstyle-example.webxml
 qtquickcontrols-imagine-automotive-example.webxml
-qtquickcontrols-imagine-musicplayer-example.webxml
-qtquickcontrols-sidepanel-example.webxml
-qtquickcontrols-swipetoremove-example.webxml
 qtquickcontrols-texteditor-example.webxml
 qtquickcontrols-wearable-example.webxml
-qtquickcontrols2-basic.webxml
-qtquickcontrols2-buttons.webxml
-qtquickcontrols2-configuration.webxml
-qtquickcontrols2-containers.webxml
-qtquickcontrols2-customize.webxml
-qtquickcontrols2-delegates.webxml
-qtquickcontrols2-deployment.webxml
-qtquickcontrols2-environment.webxml
-qtquickcontrols2-examples.webxml
-qtquickcontrols2-fileselectors.webxml
-qtquickcontrols2-focus.webxml
-qtquickcontrols2-fusion.webxml
-qtquickcontrols2-gettingstarted.webxml
-qtquickcontrols2-guidelines.webxml
-qtquickcontrols2-icons.webxml
-qtquickcontrols2-imagine.webxml
-qtquickcontrols2-indicators.webxml
-qtquickcontrols2-input.webxml
-qtquickcontrols2-macos.webxml
-qtquickcontrols2-material.webxml
-qtquickcontrols2-menus.webxml
-qtquickcontrols2-navigation.webxml
-qtquickcontrols2-popups.webxml
-qtquickcontrols2-separators.webxml
-qtquickcontrols2-styles.webxml
-qtquickcontrols2-universal.webxml
-qtquickcontrols2-windows.webxml
+qtquickcontrols-basic.webxml
+qtquickcontrols-buttons.webxml
+qtquickcontrols-configuration.webxml
+qtquickcontrols-containers.webxml
+qtquickcontrols-customize.webxml
+qtquickcontrols-delegates.webxml
+qtquickcontrols-deployment.webxml
+qtquickcontrols-environment.webxml
+qtquickcontrols-examples.webxml
+qtquickcontrols-fileselectors.webxml
+qtquickcontrols-focus.webxml
+qtquickcontrols-fusion.webxml
+qtquickcontrols-gettingstarted.webxml
+qtquickcontrols-guidelines.webxml
+qtquickcontrols-icons.webxml
+qtquickcontrols-imagine.webxml
+qtquickcontrols-indicators.webxml
+qtquickcontrols-input.webxml
+qtquickcontrols-macos.webxml
+qtquickcontrols-material.webxml
+qtquickcontrols-menus.webxml
+qtquickcontrols-navigation.webxml
+qtquickcontrols-popups.webxml
+qtquickcontrols-separators.webxml
+qtquickcontrols-styles.webxml
+qtquickcontrols-universal.webxml
+qtquickcontrols-windows.webxml
 qtquicklayouts-overview.webxml
 qtremoteobjects-cmake-qt-add-repc-merged.webxml
 qtremoteobjects-cmake-qt-add-repc-replicas.webxml
@@ -479,7 +403,6 @@ qtremoteobjects-external-schemas.webxml
 qtremoteobjects-gettingstarted.webxml
 qtremoteobjects-interaction.webxml
 qtremoteobjects-node.webxml
-qtremoteobjects-qmlmodelviewclient-example.webxml
 qtremoteobjects-registry.webxml
 qtremoteobjects-repc.webxml
 qtremoteobjects-replica.webxml
@@ -487,21 +410,11 @@ qtremoteobjects-source.webxml
 qtremoteobjects-ssl-example.webxml
 qtremoteobjects-troubleshooting.webxml
 qtremoteobjects-websockets-example.webxml
-qtscxml-calculator-qml-example.webxml
-qtscxml-calculator-widgets-example.webxml
 qtscxml-changes-qt6.webxml
 qtscxml-cmake-qt-add-statecharts.webxml
 qtscxml-ftpclient-example.webxml
 qtscxml-instantiating-state-machines.webxml
-qtscxml-invoke-dynamic-example.webxml
-qtscxml-invoke-static-example.webxml
-qtscxml-mediaplayer-qml-cppdatamodel-example.webxml
-qtscxml-mediaplayer-qml-dynamic-example.webxml
-qtscxml-mediaplayer-qml-static-example.webxml
-qtscxml-mediaplayer-widgets-dynamic-example.webxml
-qtscxml-mediaplayer-widgets-static-example.webxml
 qtscxml-overview.webxml
-qtscxml-pinball-example.webxml
 qtscxml-scxml-compliance.webxml
 qtscxml-sudoku-example.webxml
 qtscxml-trafficlight-qml-dynamic-example.webxml
@@ -509,24 +422,13 @@ qtscxml-trafficlight-qml-simple-example.webxml
 qtscxml-trafficlight-qml-static-example.webxml
 qtscxml-trafficlight-widgets-dynamic-example.webxml
 qtscxml-trafficlight-widgets-static-example.webxml
-qtsensors-accelbubble-example.webxml
 qtsensors-changes-qt6.webxml
 qtsensors-cpp.webxml
 qtsensors-examples.webxml
-qtsensors-grue-example.webxml
-qtsensors-maze-example.webxml
-qtsensors-qmlqtsensors-example.webxml
-qtsensors-sensor-explorer-example.webxml
 qtsensors-sensorsshowcase-example.webxml
 qtserialport-blockingreceiver-example.webxml
 qtserialport-blockingsender-example.webxml
-qtserialport-cenumerator-example.webxml
 qtserialport-changes-qt6.webxml
-qtserialport-creaderasync-example.webxml
-qtserialport-creadersync-example.webxml
-qtserialport-cwriterasync-example.webxml
-qtserialport-cwritersync-example.webxml
-qtserialport-enumerator-example.webxml
 qtserialport-examples.webxml
 qtserialport-terminal-example.webxml
 qtsql-cachedtable-example.webxml
@@ -538,9 +440,6 @@ qtsql-sqlbrowser-example.webxml
 qtsql-sqlwidgetmapper-example.webxml
 qtsql-tablemodel-example.webxml
 qtsvg-changes-qt6.webxml
-qtsvg-richtext-textobject-example.webxml
-qtsvg-svggenerator-example.webxml
-qtsvg-svgviewer-example.webxml
 qttest-best-practices-qdoc.webxml
 qttestlib-tutorial1-example.webxml
 qttestlib-tutorial2-example.webxml
@@ -548,7 +447,6 @@ qttestlib-tutorial3-example.webxml
 qttestlib-tutorial4-example.webxml
 qttestlib-tutorial5-example.webxml
 qttestlib-tutorial6.webxml
-qtuitools-multipleinheritance-example.webxml
 qtuitools-textfinder-example.webxml
 qtwebchannel-changes-qt6.webxml
 qtwebchannel-chatclient-html-example.webxml
@@ -560,21 +458,14 @@ qtwebengine-changes-qt6.webxml
 qtwebengine-features.webxml
 qtwebengine-overview.webxml
 qtwebengine-platform-notes.webxml
-qtwebengine-webenginequick-customdialogs-example.webxml
 qtwebengine-webenginequick-lifecycle-example.webxml
-qtwebengine-webenginequick-minimal-example.webxml
-qtwebengine-webenginequick-recipebrowser-example.webxml
-qtwebengine-webenginequick-webengineaction-example.webxml
 qtwebengine-webenginewidgets-contentmanipulation-example.webxml
 qtwebengine-webenginewidgets-cookiebrowser-example.webxml
 qtwebengine-webenginewidgets-html2pdf-example.webxml
 qtwebengine-webenginewidgets-maps-example.webxml
-qtwebengine-webenginewidgets-minimal-example.webxml
 qtwebengine-webenginewidgets-printme-example.webxml
 qtwebengine-webenginewidgets-spellchecker-example.webxml
-qtwebengine-webenginewidgets-stylesheetbrowser-example.webxml
 qtwebengine-webenginewidgets-videoplayer-example.webxml
-qtwebengine-webenginewidgets-webui-example.webxml
 qtwebenginewidgets-qtwebkitportingguide.webxml
 qtwebsockets-changes-qt6.webxml
 qtwebsockets-echoclient-example.webxml
@@ -586,57 +477,32 @@ qtwebsockets-simplechat-example.webxml
 qtwebsockets-sslechoclient-example.webxml
 qtwebsockets-sslechoserver-example.webxml
 qtwebsockets-testing.webxml
-qtwidgets-draganddrop-fridgemagnets-example.webxml
-qtwidgets-draganddrop-puzzle-example.webxml
-qtwidgets-effects-fademessage-example.webxml
 qtwidgets-gestures-imagegestures-example.webxml
 qtwidgets-graphicsview-basicgraphicslayouts-example.webxml
 qtwidgets-graphicsview-chip-example.webxml
-qtwidgets-graphicsview-embeddeddialogs-example.webxml
-qtwidgets-graphicsview-flowlayout-example.webxml
 qtwidgets-graphicsview-simpleanchorlayout-example.webxml
-qtwidgets-graphicsview-weatheranchorlayout-example.webxml
-qtwidgets-itemviews-chart-example.webxml
 qtwidgets-itemviews-coloreditorfactory-example.webxml
 qtwidgets-itemviews-combowidgetmapper-example.webxml
 qtwidgets-itemviews-customsortfiltermodel-example.webxml
-qtwidgets-itemviews-dirview-example.webxml
 qtwidgets-itemviews-frozencolumn-example.webxml
-qtwidgets-itemviews-interview-example.webxml
-qtwidgets-itemviews-pixelator-example.webxml
-qtwidgets-itemviews-puzzle-example.webxml
-qtwidgets-itemviews-simpledommodel-example.webxml
 qtwidgets-itemviews-simpletreemodel-example.webxml
-qtwidgets-itemviews-simplewidgetmapper-example.webxml
-qtwidgets-mainwindows-mainwindow-example.webxml
 qtwidgets-mainwindows-menus-example.webxml
-qtwidgets-mainwindows-sdi-example.webxml
 qtwidgets-painting-affine-example.webxml
 qtwidgets-painting-composition-example.webxml
 qtwidgets-painting-deform-example.webxml
-qtwidgets-painting-fontsampler-example.webxml
 qtwidgets-painting-gradients-example.webxml
 qtwidgets-painting-imagecomposition-example.webxml
 qtwidgets-painting-painterpaths-example.webxml
 qtwidgets-painting-pathstroke-example.webxml
 qtwidgets-painting-transformations-example.webxml
-qtwidgets-richtext-calendar-example.webxml
 qtwidgets-tools-completer-example.webxml
 qtwidgets-tools-customcompleter-example.webxml
 qtwidgets-tools-echoplugin-example.webxml
-qtwidgets-tools-i18n-example.webxml
-qtwidgets-tools-plugandpaint-app-example.webxml
-qtwidgets-tools-plugandpaint-plugins-basictools-example.webxml
-qtwidgets-tools-plugandpaint-plugins-extrafilters-example.webxml
 qtwidgets-tools-settingseditor-example.webxml
 qtwidgets-tools-styleplugin-example.webxml
 qtwidgets-tools-treemodelcompleter-example.webxml
-qtwidgets-tools-undo-example.webxml
 qtwidgets-tools-undoframework-example.webxml
-qtwidgets-touch-dials-example.webxml
-qtwidgets-touch-fingerpaint-example.webxml
 qtwidgets-touch-knobs-example.webxml
-qtwidgets-touch-pinchzoom-example.webxml
 qtwidgets-tutorials-notepad-example.webxml
 qtwidgets-tutorials-widgets-childwidget-example.webxml
 qtwidgets-tutorials-widgets-nestedlayouts-example.webxml
@@ -645,27 +511,14 @@ qtwidgets-tutorials-widgets-windowlayout-example.webxml
 qtwidgets-widgets-analogclock-example.webxml
 qtwidgets-widgets-calculator-example.webxml
 qtwidgets-widgets-calendarwidget-example.webxml
-qtwidgets-widgets-digitalclock-example.webxml
-qtwidgets-widgets-elidedlabel-example.webxml
 qtwidgets-widgets-groupbox-example.webxml
-qtwidgets-widgets-icons-example.webxml
-qtwidgets-widgets-imageviewer-example.webxml
 qtwidgets-widgets-lineedits-example.webxml
-qtwidgets-widgets-mousebuttons-example.webxml
-qtwidgets-widgets-movie-example.webxml
 qtwidgets-widgets-scribble-example.webxml
 qtwidgets-widgets-shapedclock-example.webxml
 qtwidgets-widgets-sliders-example.webxml
 qtwidgets-widgets-spinboxes-example.webxml
-qtwidgets-widgets-styles-example.webxml
-qtwidgets-widgets-stylesheet-example.webxml
 qtwidgets-widgets-tablet-example.webxml
-qtwidgets-widgets-tooltips-example.webxml
-qtwidgets-widgets-validators-example.webxml
-qtwidgets-widgets-wiggly-example.webxml
 qtwidgets-widgets-windowflags-example.webxml
-qtxml-streambookmarks-example.webxml
-qtxml-xmlstreamlint-example.webxml
 quick-changes-qt6.webxml
 qwebenginecookiestore-filterrequest.webxml
 qwidget-styling.webxml
@@ -705,9 +558,7 @@ stylesheet-syntax.webxml
 stylesheet.webxml
 svgrendering.webxml
 testlib-changes-qt6.webxml
-textedit-example.webxml
 timers.webxml
-tutorials-addressbook.webxml
 usingadaptors.webxml
 videooverview.webxml
 webengine-examples.webxml
@@ -725,7 +576,6 @@ xml-tools.webxml
 # qtdoc repository
 accessible.webxml
 appicon.webxml
-connectivity.webxml
 create-your-first-applications.webxml
 deployment.webxml
 desktop-integration.webxml
@@ -733,7 +583,6 @@ exceptionsafety.webxml
 explore-qt.webxml
 get-and-install-qt.webxml
 gettingstarted.webxml
-gettingstartedqml.webxml
 highdpi.webxml
 install-qt-design-studio.webxml
 ipc.webxml
@@ -759,7 +608,6 @@ qundo.webxml
 rcc.webxml
 restoring-geometry.webxml
 scalability.webxml
-scripting.webxml
 session.webxml
 sharedlibrary.webxml
 solutions-for-application-development.webxml
@@ -773,5 +621,4 @@ topics-data-io.webxml
 topics-ui.webxml
 uic.webxml
 unicode.webxml
-unix-signal-handlers.webxml
 wayland-and-qt.webxml
index 6c4266a12d46463dacd3b68028427cbd7703526e..33c88cfec40b0d36fc242348a33f78afbebe4dce 100644 (file)
@@ -1,7 +1,7 @@
 .. _commercial-page:
 
-Commercial Distribution
-=======================
+Commercial Use
+==============
 
 |project| follows the same licensing that Qt has, which means that there are two
 distributions, the Community Edition (LGPLv3/GPLv3) and a Commercial Edition. For
@@ -19,11 +19,14 @@ The only difference is that the ADP license **does not** include the extra
 ``Qt OPC UA``, ``Qt MQTT`` and ``Qt CoAP`` modules, which are distributed in
 a special Python wheel.
 
-All commercial licenses include the 5.15.x and 6.2.x LTS releases.
+|project| follows the same approach as Qt, meaning that commercial
+users will have access to both our commercial packages for any
+given version, or the special commercial LTS releases.
 
 Commercial users **should not** install the Community Edition distribution via ``pip
 install pyside6`` to avoid licensing problems, and should refer to the
-packages provided in the `Qt Account`_.
+packages that can be acquired from the `Qt Account`_, the Qt Installer, or
+via the `qtpip` tool.
 
 Installation
 ------------
index 093cede56a075bda2e0b7bd06bfa311b20e0f19f..45defe9e6a75f58e2a91441d80859f2ff71e4d04 100644 (file)
@@ -30,7 +30,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.ifconfig',
     'sphinx.ext.coverage', 'sphinx.ext.intersphinx', 'sphinx.ext.todo',
     'sphinx.ext.graphviz', 'inheritance_diagram', 'pysideinclude',
     'sphinx.ext.viewcode',
-    'sphinx_design', 'sphinx_copybutton', 'myst_parser']
+    'sphinx_design', 'sphinx_copybutton', 'myst_parser', 'sphinx_tags',]
 
 myst_enable_extensions = [
     "amsmath",
@@ -77,7 +77,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'PySide'
-copyright = u'2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
+copyright = u'2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
@@ -138,6 +138,11 @@ html_theme = 'furo'
 # further.  For a list of options available for each theme, see the
 # documentation.
 html_theme_options = {
+    # FIXME: This option is currently enable because on the 'requirements-doc.txt'
+    # We are using a fork of the theme, to include this new option.
+    # This needs to be removed once the functionality is either upstreamed,
+    # or a similar option is provided.
+    "collapse_navbar": True,
     "dark_css_variables": {
         "color-brand-primary": "#2cde85",
         "color-brand-content": "#2cde85",
@@ -170,7 +175,7 @@ html_logo = "@CMAKE_CURRENT_SOURCE_DIR@/_static/qtforpython.png"
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+html_favicon = "_static/qtforpython.ico"
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
@@ -229,3 +234,10 @@ if @SKIP_SPHINX_WARNINGS@:
 
 # -- Options for qthelp output ---------------------------------------------------
 qthelp_theme = 'pysidedocs_qthelp'
+
+# for example tagging based on supported platforms
+tags_create_tags = True
+tags_create_badges = True
+tags_badge_colors = {
+    "Android": "info",
+}
index 0208cae829d0c62c5f5dfd8a71555b92d19a02ed..04203a53eba61d02dabcd953ab5eda74aa1d475a 100644 (file)
@@ -4,6 +4,7 @@
     :maxdepth: 2
 
     quickstart.rst
+    commercial/index.rst
     gettingstarted/index.rst
     api.rst
     tutorials/index.rst
@@ -12,7 +13,6 @@
     deployment/index.rst
     considerations.rst
     developer/index.rst
-    commercial/index.rst
 
 ..
     Intersphinx references in toctrees is not supported
diff --git a/sources/pyside6/doc/developer/adapt_qt.rst b/sources/pyside6/doc/developer/adapt_qt.rst
new file mode 100644 (file)
index 0000000..f8582f6
--- /dev/null
@@ -0,0 +1,43 @@
+.. _developer-adapt-qt:
+
+Adapt to new Qt versions
+========================
+
+The dev branch of PySide is switched to a new Qt minor version
+after its API review is finished and the API is stable.
+
+Until that happens, a patch should be continuously developed
+to adapt to this version.
+
+The `new classes page <https://doc-snapshots.qt.io/qt6-6.7/newclasses67.html>`_
+is a good source of information for new API.
+
+New classes and should be added to the type system file (using
+a ``since`` attribute) and ``CMakeList.txt`` file of the respective module.
+
+Should the class not be available on all platforms, the respective
+``QT_CONFIG`` macro needs to be specified in the type system file and
+feature checks need to be added to ``CMakeList.txt`` (see for example
+``QPermission``).
+
+The process consists of running a build and evaluating the log file.
+The script
+`shiboken2tasks.py <https://code.qt.io/cgit/qt-creator/qt-creator.git/tree/scripts/shiboken2tasks.py>`_
+from the Qt Creator repository can be used to convert the shiboken warnings
+into a `task file <https://doc.qt.io/qtcreator/creator-task-lists.html>`_
+for display in the build issues pane of Qt Creator.
+
+Warnings about new enumerations will be shown there; they should be added
+to type system file using a ``since`` attribute.
+
+Warnings about not finding a function signature for modification
+also need to be handled; mostly this is a sign of a function parameter
+being changed from ``int`` to ``qsizetype`` or similar.
+
+If the build succeeds, a test run should be done.
+
+The Qt source code should be checked for new overloads
+(indicated by ``QT6_DECL_NEW_OVERLOAD_TAIL`` starting from 6.7).
+The resolution needs to be decided for each individual case,
+mostly by removing old functions and using ``<declare-function>``
+to declare new API.
index 321a347238c81d022c12113da381b4edefed37e1..f75aa5489f1d04158adb2cfbb4c526c2c3f3dd8b 100644 (file)
@@ -44,6 +44,15 @@ Add bindings
    of using ``#include <QtModule/header.h>`` since module include paths
    are not passed in PySide.
 
+Distribution
+------------
+
+- Determine to which wheel the module belongs according to
+  `Qt Modules <https://doc.qt.io/qt-6/qtmodules.html>`_.
+- Add the module to ``build_scripts/wheel_files.py`` for use by
+  ``create_wheels.py``.
+- Add the module to one of the ``README.pyside6_*.md`` files.
+
 Add documentation
 -----------------
 
index fdb400ec96c09886e489fdaf943178759e5d65ca..b99641f452f3ed54d926370cd4f3c8b64c708f80 100644 (file)
@@ -10,10 +10,13 @@ You can either design an example from scratch or inspired in another
 application, or simply you can port an existing Qt example that does not have
 a Python counterpart.
 
-For both cases, we recommend you to use tools like
-`flake8 <https://pypi.org/project/flake8/>`_
-(or `ruff <https://pypi.org/project/ruff/>`_) to detect issues with your code.
+Example code should be free of `flake8 <https://pypi.org/project/flake8/>`_
+warnings; this is enforced by a bot. A configuration file is provided
+at the root of the repository. Offending lines can be excluded by a
+``noqa`` directive if there is a good reason to do so.
+
 Keep in mind we do allow 100 columns for line length.
+
 Additionally, please use `isort <https://pypi.org/project/isort/>`_ to keep the
 imports ordered and consistent with other examples.
 
@@ -21,7 +24,7 @@ For example:
 
 .. code-block:: bash
 
-  $ flake8 --ignore=E266 your_file.py
+  $ flake8 --config pyside-setup/.flake8 your_file.py
   $ isort your_file.py
 
 
index 1ebfc18c57be0274c83364663772fb0f365254cd..517bd46f188e730760acdbdb533f82c257950def 100644 (file)
@@ -69,4 +69,5 @@ There are 2 scripts used for determining the inheritance:
 * ``json_inheritance.py`` (env var ``INHERITANCE_FILE``) reads a
   inheritance.json file containing the class hierarchy generated by
   shiboken's doc generator.
+
 * ``import_inheritance.py`` actually tries to import the class (legacy)
index ea0cede5185b963f7f33cbb79257eed169089a71..1509ea724f99eca34f644d795ad64019323d4c8f 100644 (file)
@@ -286,7 +286,7 @@ When using ``__feature__`` often with common IDEs, you may want to provide
 a feature-aware version of ``.pyi`` files to get a correct display. The simplest
 way to change them all in-place is the command:
 
-.. code-block:: python
+.. code-block:: bash
 
     pyside6-genpyi all --feature snake_case true_property
 
index d15b36bc34e0a8524451fef86baf0111d2d0cac1..92c84259d93535de92f85546a723338229d6336b 100644 (file)
@@ -17,6 +17,7 @@ Development Topics
    add_port_example.rst
    add_tool.rst
    documentation.rst
+   adapt_qt.rst
    extras.rst
 
 Implementation details
index c81e9a5593ddf0163715be3e05aedd196d96bcb1..a6c703dabb0f74fe6716b5aefaae701c237c55e5 100644 (file)
@@ -318,7 +318,7 @@ A shortcut for this command is ``pyside6-genpyi``.
 
 A useful command to change all .pyi files to use all features is
 
-.. code-block:: python
+.. code-block:: bash
 
     pyside6-genpyi all --feature snake_case true_property
 
index dda05f20b9c1c8dbce17cb8d908afe5704e75811..96589675299fe2022099136041de33a725252ffc 100644 (file)
@@ -56,7 +56,7 @@ the environment variable ``QT_EVENT_DISPATCHER_CORE_FOUNDATION=1`` to
 circumvent this issue.
 
 Applications that don't use Classic Bluetooth will find a subset of
-`QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ is available,
+`QtBluetooth`_ is available,
 as CoreBluetooth (Bluetooth LE) do not require either of
 :class:`QApplication<PySide6.QtWidgets.QApplication>` or
 :class:`QGuiApplication<PySide6.QtGui.QGuiApplication>` .
@@ -71,32 +71,32 @@ Guides
 Logging Categories
 ^^^^^^^^^^^^^^^^^^
 
-The `QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ module
+The `QtBluetooth`_ module
 exports the following :class:`logging categories<~.Configuring Categories>` :
 
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |Logging Category    |Description                                                                                                   |
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |qt.bluetooth        |Enables logging of cross platform code path in `QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_|
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |qt.bluetooth.android|Enables logging of the Android implementation                                                                 |
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |qt.bluetooth.bluez  |Enables logging of the BLuez/Linux implementation                                                             |
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |qt.bluetooth.ios    |Enables logging of the iOS implementation                                                                     |
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |qt.bluetooth.osx    |Enables logging of the macOS implementation                                                                   |
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
-    |qt.bluetooth.windows|Enables logging of the Qt for Windows implementation                                                          |
-    +--------------------+--------------------------------------------------------------------------------------------------------------+
+    +--------------------+-------------------------------------------------------------+
+    |Logging Category    |Description                                                  |
+    +--------------------+-------------------------------------------------------------+
+    |qt.bluetooth        |Enables logging of cross platform code path in `QtBluetooth`_|
+    +--------------------+-------------------------------------------------------------+
+    |qt.bluetooth.android|Enables logging of the Android implementation                |
+    +--------------------+-------------------------------------------------------------+
+    |qt.bluetooth.bluez  |Enables logging of the BLuez/Linux implementation            |
+    +--------------------+-------------------------------------------------------------+
+    |qt.bluetooth.ios    |Enables logging of the iOS implementation                    |
+    +--------------------+-------------------------------------------------------------+
+    |qt.bluetooth.osx    |Enables logging of the macOS implementation                  |
+    +--------------------+-------------------------------------------------------------+
+    |qt.bluetooth.windows|Enables logging of the Qt for Windows implementation         |
+    +--------------------+-------------------------------------------------------------+
 
 Logging categories can be used to enable additional warning and debug output
-for `QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ . More
-detailed information about logging can be found in
+for `QtBluetooth`_ . More detailed information about logging can be found in
 :class:`QLoggingCategory<~.QLoggingCategory>` . A quick way to enable all
-`QtBluetooth <https://doc.qt.io/qt-6/qtbluetooth-module.html>`_ logging is to
-add the following line to the ``main()`` function:
+`QtBluetooth`_ logging is to add the following line to the ``main()``
+function::
 
-    ::
+    QLoggingCategory.setFilterRules("qt.bluetooth* = true")
 
-            QLoggingCategory.setFilterRules("qt.bluetooth* = true")
+
+.. _QtBluetooth: https://doc.qt.io/qt-6/qtbluetooth-module.html
index 0cb6d32edc057ed60527a430e1767f20b1e7044b..aa080aa087f10499b26e5a1eb026a50b50719c06 100644 (file)
@@ -22,7 +22,7 @@ value and enabling special characters in the key.
 Example
 -------
 
-::
+.. code-block:: python
 
     # Recommended syntax
     @ClassInfo(Author='PySide Team', URL='http://www.pyside.org')
index f6f7bd4e71243c2c1d86c89d5bb7d9522a515735..64567c4bcd51b94d9af4597b3b1d6fc2c147c5a9 100644 (file)
@@ -16,7 +16,7 @@ They are equivalent to the ``Q_PROPERTY`` macro in the `Qt Docs`_.
 Here is an example that illustrates how to use this
 function:
 
-.. code-block::
+.. code-block:: python
    :linenos:
 
     from PySide6.QtCore import QObject, Property
@@ -40,20 +40,20 @@ function:
 
 The full options for ``QtCore.Property`` can be found with ``QtCore.Property.__doc__``:
 
-.. code-block::
-
-   Property(self, type: type,
-            fget: Optional[Callable] = None,
-            fset: Optional[Callable] = None,
-            freset: Optional[Callable] = None,
-            fdel: Optional[Callable] = None,
-            doc: str = '',
-            notify: Optional[Callable] = None,
-            designable: bool = True,
-            scriptable: bool = True,
-            stored: bool = True, user: bool = False,
-            constant: bool = False,
-            final: bool = False) -> PySide6.QtCore.Property
+.. code-block:: python
+
+    Property(self, type: type,
+             fget: Optional[Callable] = None,
+             fset: Optional[Callable] = None,
+             freset: Optional[Callable] = None,
+             fdel: Optional[Callable] = None,
+             doc: str = '',
+             notify: Optional[Callable] = None,
+             designable: bool = True,
+             scriptable: bool = True,
+             stored: bool = True, user: bool = False,
+             constant: bool = False,
+             final: bool = False) -> PySide6.QtCore.Property
 
 Normally, only ``type``, ``fget``and ``fset`` are used.
 
@@ -68,16 +68,16 @@ requires a ``type`` parameter.
 
 In the above example, the following lines would be equivalent properties:
 
-.. code-block::
+.. code-block:: python
 
-        pp = QtCore.Property(int, readPP, setPP)    # PySide version
-        pp = property(readPP, setPP)                # Python version
+    pp = QtCore.Property(int, readPP, setPP)    # PySide version
+    pp = property(readPP, setPP)                # Python version
 
 As you know from the `Python Docs`_, ``Python`` allows to break the property
 creation into multiple steps, using the decorator syntax. We can do this in
 ``PySide`` as well:
 
-.. code-block::
+.. code-block:: python
    :linenos:
 
     from PySide6.QtCore import QObject, Property
@@ -110,7 +110,7 @@ If you are using properties of your objects in QML expressions,
 QML requires that the property changes are notified. Here is an
 example illustrating how to do this:
 
-.. code-block::
+.. code-block:: python
    :linenos:
 
     from PySide6.QtCore import QObject, Signal, Property
index b50c34148b4c38c1e55f8f7ebb5b2092ff84fc5a..43dd4921627b36f4ead42df13b6bad56f2dc5b17 100644 (file)
@@ -1,8 +1,8 @@
 .. currentmodule:: PySide6.QtCore
 .. _Signal:
 
-Signal
-******
+Qt Signal
+*********
 
 Synopsis
 --------
@@ -10,19 +10,19 @@ Synopsis
 Functions
 ^^^^^^^^^
 
-+---------------------------------------------------------------------------------------------+
-|def :meth:`connect<Signal.connect>` (receiver)                                               |
-+---------------------------------------------------------------------------------------------+
-|def :meth:`disconnect<Signal.disconnect>` (receiver)                                         |
-+---------------------------------------------------------------------------------------------+
-|def :meth:`emit<Signal.disconnect>` (\*args)                                                 |
-+---------------------------------------------------------------------------------------------+
++-----------------------------------------------------+
+|def :meth:`connect<Signal.connect>` (receiver)       |
++-----------------------------------------------------+
+|def :meth:`disconnect<Signal.disconnect>` (receiver) |
++-----------------------------------------------------+
+|def :meth:`emit<Signal.disconnect>` (\*args)         |
++-----------------------------------------------------+
 
 Detailed Description
 --------------------
 
-    The :class:`~.Signal` class provides a way to declare and connect Qt
-    signals in a pythonic way.
+The :class:`~.Signal` class provides a way to declare and connect Qt
+signals in a pythonic way.
 
 .. class:: PySide6.QtCore.Signal([type1 [, type2...]] [, name="" [, arguments=[]]])
 
index c62104369191bd2cc37b2eaa040f400cba850f1c..06646e33c5f825e3e9fa9546fcb47c96793d7c58 100644 (file)
@@ -1,8 +1,8 @@
 .. currentmodule:: PySide6.QtCore
 .. _Slot:
 
-Slot
-****
+Qt Slots
+********
 
 Detailed Description
 --------------------
index 1baa9e369af72b65ccb13c4408cb44a8fbcb2025..5280e5e2237e095cf0428873b4b902bd17e614dc 100644 (file)
@@ -5,6 +5,8 @@ QPyDesignerContainerExtension
 *****************************
 
 QPyDesignerContainerExtension is the base class for implementing
-`QDesignerContainerExtension <https://doc.qt.io/qt-6/qdesignercontainerextension.html>`_
+`QDesignerContainerExtension class`_
 for a Qt Designer custom widget plugin in Python.
 It provides the required inheritance from **QObject**.
+
+.. _QDesignerContainerExtension class: https://doc.qt.io/qt-6/qdesignercontainerextension.html
index 00260860b774817e2e8b9af83375fed8ff5454c3..71ef6cd69479b5740c9384154e039b26fbade665 100644 (file)
@@ -19,20 +19,20 @@ Functions
 Detailed Description
 --------------------
 
-    The :class:`~.QPyDesignerCustomWidgetCollection` implements
-    `QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
-    and provides static helper functions for registering custom widgets by
-    type or by implementing
-    `QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .
+The :class:`~.QPyDesignerCustomWidgetCollection` implements
+`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
+and provides static helper functions for registering custom widgets by
+type or by implementing
+`QDesignerCustomWidgetInterface`_ .
 
-    The usage is explained in :ref:`designer_custom_widgets`.
+The usage is explained in :ref:`designer_custom_widgets`.
 
 .. py:staticmethod:: QPyDesignerCustomWidgetCollection.registerCustomWidget(type[, xml=""[, tool_tip=""[, icon=""[, group=""[container=False]]]]])
 
    Registers an instance of a Python-implemented QWidget by type with Qt Designer.
 
    The optional keyword arguments correspond to the getters of
-   `QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ :
+   `QDesignerCustomWidgetInterface`_ :
 
    :param str xml: A snippet of XML code in ``.ui`` format that specifies how the widget is created and sets initial property values.
    :param str tool_tip: Tool tip to be shown in the widget box.
@@ -46,7 +46,9 @@ Detailed Description
 .. py:staticmethod:: QPyDesignerCustomWidgetCollection.addCustomWidget(custom_widget)
 
     Adds a custom widget (implementation of
-    `QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_)
+    `QDesignerCustomWidgetInterface`_)
     with Qt Designer.
 
    :param QDesignerCustomWidgetInterface custom_widget: Custom widget instance
+
+.. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
index c52aafdf52b01190c433be5f6e37831dce61a5d2..997da1480ca233dc74b0d07e111fcc362614299f 100644 (file)
@@ -5,6 +5,8 @@ QPyDesignerMemberSheetExtension
 *******************************
 
 QPyDesignerMemberSheetExtension is the base class for implementing
-`QDesignerMemberSheetExtension <https://doc.qt.io/qt-6/qdesignermembersheetextension.html>`_
+`QDesignerMemberSheetExtension class`_
 for a Qt Designer custom widget plugin in Python.
 It provides the required inheritance from **QObject**.
+
+.. _QDesignerMemberSheetExtension class: https://doc.qt.io/qt-6/qdesignermembersheetextension.html
index 004c94693d4a7c300eba464bf910f13a9a935f5a..84d19c544024c6a3503bd207940dda239328e3d3 100644 (file)
@@ -5,6 +5,8 @@ QPyDesignerTaskMenuExtension
 ****************************
 
 QPyDesignerTaskMenuExtension is the base class for implementing
-`QDesignerTaskMenuExtension <https://doc.qt.io/qt-6/qdesignertaskmenuextension.html>`_
+`QDesignerTaskMenuExtension class`_
 for a Qt Designer custom widget plugin in Python.
 It provides the required inheritance from **QObject**.
+
+.. _QDesignerTaskMenuExtension class: https://doc.qt.io/qt-6/qdesignertaskmenuextension.html
index f495ddc4e2dc68b76f3f71d30f48da1b5c954911..527c2427bec67c103f6305cc4d64b16d66c56bc2 100644 (file)
@@ -29,7 +29,7 @@ directive:
 
     import PySide6.QtMultimedia
 
-The module also provides `QML types <https://doc.qt.io/qt-6/qtmultimedia-qmlmodule.html>`_ .
+The module also provides QML `types <https://doc.qt.io/qt-6/qtmultimedia-qmlmodule.html>`_ .
 
 Overviews and Important Topics
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -46,27 +46,38 @@ QML Types
 
 The following table outlines some important QML types.
 
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |Type                                                                           |Description                                                                                                                                                                                                                                             |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`MediaPlayer <https://doc.qt.io/qt-6/qml-qtmultimedia-mediaplayer.html>`_      |Add audio/video playback functionality to a scene.                                                                                                                                                                                                      |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`CaptureSession <https://doc.qt.io/qt-6/qml-qtmultimedia-capturesession.html>`_|Create a session for capturing audio/video.                                                                                                                                                                                                             |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`Camera <https://doc.qt.io/qt-6/qml-qtmultimedia-camera.html>`_                |Access a camera connected to the system.                                                                                                                                                                                                                |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`AudioInput <https://doc.qt.io/qt-6/qml-qtmultimedia-audioinput.html>`_        |Access an audio input (microphone) connected to the system.                                                                                                                                                                                             |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`AudioOutput <https://doc.qt.io/qt-6/qml-qtmultimedia-audiooutput.html>`_      |Access an audio output (speaker, headphone) connected to the system.                                                                                                                                                                                    |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`VideoOutput <https://doc.qt.io/qt-6/qml-qtmultimedia-videooutput.html>`_      |Display video content.                                                                                                                                                                                                                                  |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`MediaRecorder <https://doc.qt.io/qt-6/qml-qtmultimedia-mediarecorder.html>`_  |Record audio/video from the `CaptureSession <https://doc.qt.io/qt-6/qml-qtmultimedia-capturesession.html>`_ .                                                                                                                                           |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`ImageCapture <https://doc.qt.io/qt-6/qml-qtmultimedia-imagecapture.html>`_    |Capture still images from the Camera.                                                                                                                                                                                                                   |
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-    |`Video <https://doc.qt.io/qt-6/qml-qtmultimedia-video.html>`_                  |Add Video playback functionality to a scene. Uses `MediaPlayer <https://doc.qt.io/qt-6/qml-qtmultimedia-mediaplayer.html>`_ and `VideoOutput <https://doc.qt.io/qt-6/qml-qtmultimedia-videooutput.html>`_ types to provide video playback functionality.|
-    +-------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+    +------------------+---------------------------------------------------------------------+
+    |Type              |Description                                                          |
+    +------------------+---------------------------------------------------------------------+
+    |`MediaPlayer`_    |Add audio/video playback functionality to a scene.                   |
+    +------------------+---------------------------------------------------------------------+
+    |`CaptureSession`_ |Create a session for capturing audio/video.                          |
+    +------------------+---------------------------------------------------------------------+
+    |`Camera`_         |Access a camera connected to the system.                             |
+    +------------------+---------------------------------------------------------------------+
+    |`AudioInput`_     |Access an audio input (microphone) connected to the system.          |
+    +------------------+---------------------------------------------------------------------+
+    |`AudioOutput`_    |Access an audio output (speaker, headphone) connected to the system. |
+    +------------------+---------------------------------------------------------------------+
+    |`VideoOutput`_    |Display video content.                                               |
+    +------------------+---------------------------------------------------------------------+
+    |`MediaRecorder`_  |Record audio/video from the `CaptureSession`_ .                      |
+    +------------------+---------------------------------------------------------------------+
+    |`ImageCapture`_   |Capture still images from the Camera.                                |
+    +------------------+---------------------------------------------------------------------+
+    |`Video`_          |Add Video playback functionality to a scene. Uses `MediaPlayer`_ and |
+    |                  |`VideoOutput`_ types to provide video playback functionality.        |
+    +------------------+---------------------------------------------------------------------+
+
+.. _MediaPlayer: https://doc.qt.io/qt-6/qml-qtmultimedia-mediaplayer.html
+.. _CaptureSession: https://doc.qt.io/qt-6/qml-qtmultimedia-capturesession.html
+.. _Camera: https://doc.qt.io/qt-6/qml-qtmultimedia-camera.html
+.. _AudioInput: https://doc.qt.io/qt-6/qml-qtmultimedia-audioinput.html
+.. _AudioOutput: https://doc.qt.io/qt-6/qml-qtmultimedia-audiooutput.html
+.. _VideoOutput: https://doc.qt.io/qt-6/qml-qtmultimedia-videooutput.html
+.. _MediaRecorder: https://doc.qt.io/qt-6/qml-qtmultimedia-mediarecorder.html
+.. _ImageCapture: https://doc.qt.io/qt-6/qml-qtmultimedia-imagecapture.html
+.. _Video: https://doc.qt.io/qt-6/qml-qtmultimedia-video.html
 
 C++ Classes
 ^^^^^^^^^^^
index 58f609b03197915014859ed63dcf0d02e4f649aa..f027885afa603b6e7055ba7fcdafad8f07ee9685 100644 (file)
@@ -5,6 +5,8 @@ QPyQmlParserStatus
 ******************
 
 QPyQmlParserStatus is the base class for implementing
-`QQmlParserStatus <https://doc.qt.io/qt-6/qqmlparserstatus.html>`_
+`QQmlParserStatus class`_ .
 
 It provides the required inheritance from **QObject**.
+
+.. _QQmlParserStatus class: https://doc.qt.io/qt-6/qqmlparserstatus.html
index 576b8baa07435c750ad05fa9121f77adc11b8526..fea238827b2c12ba569b546067e815dadfa63a0b 100644 (file)
@@ -5,6 +5,8 @@ QPyQmlPropertyValueSource
 *************************
 
 QPyQmlPropertyValueSource is the base class for implementing
-`QQmlPropertyValueSource <https://doc.qt.io/qt-6/qqmlpropertyvaluesource.html>`_
+`QQmlPropertyValueSource class`_ .
 
 It provides the required inheritance from **QObject**.
+
+.. _QQmlPropertyValueSource class: https://doc.qt.io/qt-6/qqmlpropertyvaluesource.html
index d3d3bf4a78931b910b1edfda635065383e4e2b6a..a2430389b99610e7408e5024bc05984fa58c0186 100644 (file)
@@ -1,8 +1,8 @@
 .. currentmodule:: PySide6.QtQml
 .. _QmlSingleton:
 
-QmlSingleton
-************
+QmlSingleton decorator
+**********************
 
 .. py:decorator:: QmlSingleton
 
@@ -20,6 +20,6 @@ constructor.
     @QmlElement
     @QmlSingleton
     class ClassForQml(QObject):
-        ...
+        ...
 
 .. note:: The order of the decorators matters; ``QmlSingleton`` needs to be preceded by ``QmlElement``.
index 72fde741dbdf27ab9165778247968c5434f67d66..46c7c49decdd21e84be859e1bda6ed96ae3871c8 100644 (file)
@@ -26,6 +26,6 @@ Passing None or no argument will cause a standard message to be used instead.
     @QmlElement
     @QmlUncreatable("BaseClassForQml is an abstract base class")
     class BaseClassForQml(QObject):
-        ...
+        ...
 
 .. note:: The order of the decorators matters; ``QmlUncreatable`` needs to be preceded by ``QmlElement``.
index 8a8c4bef0958446d30102eeabf25d564c229cb0f..48879d263859d9fac917b8d07510232a8897a487 100644 (file)
@@ -1,5 +1,4 @@
 .. currentmodule:: PySide6.QtUiTools
-.. _loadUiType:
 
 loadUiType
 ***********
index 71d38f80955c3d563e0bfca3a0fa35157f1555b4..d3bd4512d2ff4ee89a84a0c37d6c4a488c6954c1 100644 (file)
@@ -58,7 +58,6 @@ XML. Here is an extract of the beginning of a ``.ui`` file:
        <string>MainWindow</string>
       </property>
       <widget class="QWidget" name="centralWidget">
-    ...
 
 The `pyside6-uic` tool generates Python code from these `.ui` files,
 which you can import from your main files, so it is not necessary
index e867fadd45389c7f9841725d404908ae5c27444a..bf6d19ab308cef04d91c50b8c03ca375c08bd811 100644 (file)
@@ -4,11 +4,9 @@ Binding Generation: What Is Shiboken?
 =====================================
 
 When you install ``PySide6`` you might have notice that also ``Shiboken6``
-is installed as a dependency:
+is installed as a dependency::
 
-.. code-block:: bash
-
-    (env) [qt ~]$ pip install pyside6
+    $ pip install pyside6
     Collecting pyside6
       Downloading PySide6-6.0.0-6.0.0-cp36.cp37.cp38.cp39-abi3-manylinux1_x86_64.whl (170.5 MB)
          |████████████████████████████████| 170.5 MB 42 kB/s
index 111085addf64e7f3f2c3c283f02c687584780d0b..912105ef85e4210b44a64d4cfd3ef4f90c8ada52 100644 (file)
@@ -86,7 +86,7 @@ Assumming that Qt is in PATH, for example, the configure step can be done with::
     cmake -B /path/to/the/build/directory \
           -S /path/to/the/pyside-setup \
           -DCMAKE_INSTALL_PREFIX=/where/to/install \
-          -DPYTHON_EXECUTABLE=/path/to/interpreter
+          -DPython_EXECUTABLE=/path/to/interpreter
 
 .. note:: You can add `-DFORCE_LIMITED_API=yes` in case you want to have a
    build which will be compatible with Python 3.7+.
index 07f5b48c66e1ea778ad7299aff56c37051c496de..6699f54e05ca840829a67ed4908d0d615a7228be 100644 (file)
@@ -36,14 +36,14 @@ import statements:
 
 .. code-block:: python
 
-    from PySide2.QtWidgets import QApplication...
+    from PySide2.QtWidgets import QApplication
     from PySide2 import QtCore
 
 needs to be changed to:
 
 .. code-block:: python
 
-    from PySide6.QtWidgets import QApplication...
+    from PySide6.QtWidgets import QApplication
     from PySide6 import QtCore
 
 
index 2911b458220e728961ca9a9e053c3d83c868001b..e45ebb176ab579923a61b228790eb84c393563bb 100644 (file)
@@ -16,6 +16,22 @@ python inheritance_graph.py  PySide6.QtWidgets PySide6.QtWidgets.QWizard
 """
 
 
+def format_dict(d):
+    """Format the URL dict for error message."""
+    result = '{'
+    n = 0
+    for k, v in d.items():
+        n += 1
+        if n > 10:
+            result += "..."
+            break
+        if n > 1:
+            result += ", "
+        result += f'"{k}": "{v}"'
+    result += '}'
+    return result
+
+
 class InheritanceGraph(object):
     """
     Given a list of classes, determines the set of classes that they inherit
@@ -104,6 +120,10 @@ class InheritanceGraph(object):
             if url is not None:
                 this_node_attrs['URL'] = f'"{url}"'
                 this_node_attrs['target'] = '"_top"'  # Browser target frame attribute (same page)
+            else:
+                urls_str = format_dict(urls)
+                print(f'inheritance_graph.py: No URL found for {name} ({fullname}) in {urls_str}.',
+                      file=sys.stderr)
             attribute = self._format_node_attrs(this_node_attrs)
             res.append(f'  "{name}" [{attribute}];\n')
 
index 625d60a750da93da3b24a7fc5ba2af3c8e48e9ce..bffb49b7e7517569470d486a87b7ecb1dcf8d0b2 100644 (file)
@@ -224,3 +224,7 @@ Qt Modules Supported by Qt for Python
     .. grid-item-card:: :mod:`Qt3D Render <PySide6.Qt3DRender>`
 
         Contains functionality to support 2D and 3D rendering using Qt 3D.
+
+    .. grid-item-card:: :mod:`QtAsyncio <PySide6.QtAsyncio>`
+
+        Provides integration between asyncio and Qt's event loop.
diff --git a/sources/pyside6/doc/qdoc_spawner.py.in b/sources/pyside6/doc/qdoc_spawner.py.in
new file mode 100644 (file)
index 0000000..0223e50
--- /dev/null
@@ -0,0 +1,99 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+import argparse
+import subprocess
+import os
+import sys
+import time
+from multiprocessing import Pool, cpu_count
+from pathlib import Path
+
+
+def run_qdoc(file, qdoc_args, args):
+    env = os.environ
+    env["BUILDDIR"] = args.build_dir
+    env["QT_INSTALL_DOCS"] = args.qt_install_docs
+    env["QT_VERSION"] = args.qt_version
+    env["QT_VER"] = ".".join(args.qt_version.split(".")[:2])
+    env["QT_VERSION_TAG"] = args.qt_version
+
+    command = [
+        args.qdoc_bin,
+        file,
+        *qdoc_args,
+        "-installdir",
+        args.doc_data_dir,
+        "-outputdir",
+        args.doc_data_dir,
+    ]
+
+    start_time = time.time()
+    _ = subprocess.Popen(command, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    out, err = _.communicate()
+    returncode = _.wait()
+    duration = round(time.time() - start_time)
+
+    if args.verbose or returncode != 0 or err:
+       cmd_str = " ".join(command)
+       print(f"> Output of: {cmd_str}")
+       if out:
+           print(out.decode("utf-8"), file=sys.stdout)
+       if err:
+            print(err.decode("utf-8"), file=sys.stderr)
+       print(f"> Finished: {file} {duration}s (exit code {returncode})")
+
+    if returncode != 0:
+       raise Exception(f"Failing executing the command {command} ({returncode})")
+
+
+def get_qdocconf_files():
+    if not Path("pyside.qdocconf").exists():
+        print("ERROR: the working dir doesn't include a 'pyside.qdocconf' file")
+        sys.exit(-1)
+
+    # Generate the temporary qdocconf files
+    # This is necessary because using a file like 'pyside-qtcore.qtdocconf'
+    # will generate an error, because inside we call functions like 'include()'
+    files_single_exec = []
+    files_prepare = []
+    with open("pyside.qdocconf") as f:
+        for i in f.read().splitlines():
+            _p = Path(i)
+            _name = f"_{_p.stem}.qdocconf"
+            with open(_name, "w", encoding="utf-8") as f:
+                f.write(i)
+            files_single_exec.append(_name)
+            files_prepare.append(i.strip())
+
+    return files_prepare, files_single_exec
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(prog="qdoc spawner")
+    parser.add_argument("--qt", dest="qt_version", action="store", required=True)
+    parser.add_argument("--doc-data-dir", dest="doc_data_dir", action="store", required=True)
+    parser.add_argument("--qdoc-binary", dest="qdoc_bin", action="store", required=True)
+    parser.add_argument("--build-dir", dest="build_dir", action="store", required=True)
+    parser.add_argument("--qt-install-docs", dest="qt_install_docs", action="store", required=True)
+    parser.add_argument("--parallel", dest="parallel", action="store", default="4")
+    parser.add_argument("--verbose", dest="verbose", action="store_true", default=False)
+
+    args = parser.parse_args()
+    files_prepare, files_single_exec = get_qdocconf_files()
+
+    parallel = args.parallel
+    if parallel == "auto":
+        parallel = cpu_count()
+
+    try:
+        # mode: -prepare -no-link-errors
+        with Pool(int(parallel)) as p:
+            p.starmap(run_qdoc, [(str(f), ["-prepare", "-no-link-errors"], args) for f in files_prepare])
+
+        # mode: -single-exec
+        with Pool(int(parallel)) as p:
+            p.starmap(run_qdoc, [(str(f), ["-single-exec"], args) for f in files_single_exec])
+    except Exception as e:
+        print(f"qdoc_spawner: error: {e}", file=sys.stderr)
+        sys.exit(-1)
index e5b13cc4a5a380c193e07276cae71b458a5b1714..7e1a210f2918686dacbaf2be9f6066d50a40bbb1 100644 (file)
@@ -28,6 +28,8 @@ Before you can install |project|, first you must install the following software:
 Installation
 ------------
 
+.. note:: For a commercial installation, refer to :ref:`commercial-page`.
+
 * **Creating and activating an environment**
   You can do this by running the following on a terminal:
 
index 2221858bc0f9e1f90fa18dbf5d36cbec3ed84cd7..c5464640b6378f5f82cbd857ba8da3a3f1ce845f 100644 (file)
@@ -60,7 +60,8 @@ signal to the `say_hello()` function:
     button.clicked.connect(say_hello)
 
 Finally, we show the button and start the Qt main loop:
-::
+
+.. code-block:: python
 
     # Show the button
     button.show()
@@ -68,9 +69,8 @@ Finally, we show the button and start the Qt main loop:
     app.exec()
 
 Here is the complete code for this example:
-::
 
-    #!/usr/bin/python
+.. code-block:: python
 
     import sys
     from PySide6.QtWidgets import QApplication, QPushButton
index e2512f9db9facdc239b7bb03173856a79b9a5d45..b7712672bfa58bfeb84e53c4e0df7e33180ee3c8 100644 (file)
@@ -105,7 +105,8 @@ Complete code
 -------------
 
 Here is the complete code for this tutorial:
-::
+
+.. code-block:: python
 
     import sys
     from PySide6.QtWidgets import (QLineEdit, QPushButton, QApplication,
index e4f06f43748a97e3ff0dffbf568ed190a234f459..6ca403ba7b1e2e33f24cea717b89b9d0c5e2bd22 100644 (file)
@@ -9,10 +9,10 @@ Translating Applications
 Qt Linguist
 -----------
 
-`Qt Linguist <https://doc.qt.io/qt-6/qtlinguist-index.html>`_ and
+`Qt Linguist`_ and
 its related tools can be used to provide translations for applications.
 
-The ``examples/widgets/linguist`` example illustrates this. The example is
+The :ref:`qt-linguist-example` example illustrates this. The example is
 very simple, it has a menu and shows a list of programming languages with
 multiselection.
 
@@ -98,13 +98,15 @@ The example can then be run in German:
 
     LANG=de python main.py
 
+.. _Qt Linguist: https://doc.qt.io/qt-6/qtlinguist-index.html
+
 GNU gettext
 -----------
 
-The `GNU gettext <https://docs.python.org/3/library/gettext.html>`_ module
+The `GNU gettext`_ module
 can be used to provide translations for applications.
 
-The ``examples/widgets/gettext`` example illustrates this. The example is
+The :ref:`gettext-example` example illustrates this. The example is
 very simple, it has a menu and shows a list of programming languages with
 multiselection.
 
@@ -119,7 +121,7 @@ Those functions are defined at the top:
 .. code-block:: python
 
     import gettext
-    ...
+    ...
     _ = None
     ngettext = None
 
@@ -212,3 +214,5 @@ The example can then be run in German:
 .. code-block:: bash
 
     LANG=de python main.py
+
+.. _GNU gettext: https://docs.python.org/3/library/gettext.html
index b286de507b95bb28dd0f7dd4abda46ba70e34609..f431cb5c46f63560c911a6bf4aa606240591161e 100644 (file)
@@ -11,14 +11,14 @@ information in trees. You can also create a data model and display it using a
    further on. To know more about the Model/View architecture in Qt, refer to
    its `official documentation <https://doc.qt.io/qt-6/model-view-programming.html>`_.
 
-1. Import ``QTreeWidget`` and ``QTreeWidgetItem`` for this application:
+#. Import ``QTreeWidget`` and ``QTreeWidgetItem`` for this application:
 
    .. code-block:: python
 
        import sys
        from PySide6.QtWidgets import QApplication, QTreeWidget, QTreeWidgetItem
 
-2. Define a dictionary with project structures to display the information as a
+#. Define a dictionary with project structures to display the information as a
    tree, with files belonging to each project:
 
    .. code-block:: python
@@ -27,13 +27,13 @@ information in trees. You can also create a data model and display it using a
                "Project B": ["file_b.csv", "photo.jpg"],
                "Project C": []}
 
-3. Initialize the ``QApplication`` singleton:
+#. Initialize the ``QApplication`` singleton:
 
    .. code-block:: python
 
        app = QApplication()
 
-4. Configure the ``QTreeWidget`` to have two columns, one for the item name,
+#. Configure the ``QTreeWidget`` to have two columns, one for the item name,
    and the other for item type information of the files in the project
    directories.
    You can set the column name with the ``setHeaderLabels`` as described below:
@@ -44,7 +44,7 @@ information in trees. You can also create a data model and display it using a
        tree.setColumnCount(2)
        tree.setHeaderLabels(["Name", "Type"])
 
-5. Iterate the data structure, create the ``QTreeWidgetItem`` elements, and add
+#. Iterate the data structure, create the ``QTreeWidgetItem`` elements, and add
    the corresponding children to each parent.
    We also extract the extension name for only the files and add them
    into the second column.
@@ -64,7 +64,7 @@ information in trees. You can also create a data model and display it using a
 
        tree.insertTopLevelItems(0, items)
 
-7. Show the tree and execute the ``QApplication``.
+#. Show the tree and execute the ``QApplication``.
 
    .. code-block:: python
 
index aedaf2d60f96f32c17ffc38ebb8e6404ed67396e..c56593ee4e0d17fa75505ef95b444a42652a5343 100644 (file)
@@ -192,7 +192,7 @@ The complete code of this example looks like this:
 Then to execute it we just need to run the following on a
 command prompt:
 
-.. code-block:: python
+.. code-block:: bash
 
     python main.py
 
@@ -214,7 +214,7 @@ in the widget box and can be dragged onto the form just like Qt's widgets (see
 `Using Custom Widgets with Qt Designer <https://doc.qt.io/qt-6/designer-using-custom-widgets.html>`_
 ). Normally, this requires implementing the widget as a plugin to Qt Designer
 written in C++ implementing its
-`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .
+`QDesignerCustomWidgetInterface`_ .
 
 Qt for Python provides a simple interface for this which is similar to
 :meth:`registerCustomWidget()<PySide6.QtUiTools.QUiLoader.registerCustomWidget>`.
@@ -262,10 +262,10 @@ The code of the registration script looks as follows:
 
 
 QPyDesignerCustomWidgetCollection provides an implementation of
-`QDesignerCustomWidgetCollectionInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html>`_
+`QDesignerCustomWidgetCollectionInterface`_
 exposing custom widgets to **Qt Designer** with static convenience functions
 for registering types or adding instances of
-`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ .
+`QDesignerCustomWidgetInterface`_ .
 
 The function
 :meth:`registerCustomWidget()<PySide6.QtDesigner.QPyDesignerCustomWidgetCollection.registerCustomWidget>`
@@ -273,7 +273,7 @@ is used to register a widget type with **Qt Designer**. In the simple case, it
 can be used like ``QUiLoader.registerCustomWidget()``. It takes the custom widget
 type and some optional keyword arguments passing values that correspond to the
 getters of
-`QDesignerCustomWidgetInterface <https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html>`_ :
+`QDesignerCustomWidgetInterface`_ :
 
 When launching **Qt Designer** via its launcher ``pyside6-designer``,
 the custom widget should be visible in the widget box.
@@ -286,6 +286,9 @@ is registered for the custom widget. The example is a port of the
 corresponding C++
 `Task Menu Extension Example <https://doc.qt.io/qt-6/qtdesigner-taskmenuextension-example.html>`_ .
 
+.. _QDesignerCustomWidgetCollectionInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetcollectioninterface.html
+.. _QDesignerCustomWidgetInterface: https://doc.qt.io/qt-6/qdesignercustomwidgetinterface.html
+
 Troubleshooting the Qt Designer Plugin
 ++++++++++++++++++++++++++++++++++++++
 
index c674a61169e28b03e916932e01cd55634122b8e2..2064488ae9a843ad3b47e3589ef5ebbcd88e5201 100644 (file)
@@ -33,10 +33,10 @@ code block.
 .. code-block:: python
    :linenos:
 
-     if __name__ == "__main__":
-         app = QApplication([])
-         # ...
-         sys.exit(app.exec())
+    if __name__ == "__main__":
+        app = QApplication([])
+        # ...
+        sys.exit(app.exec())
 
 Now, to start the development, create an empty window called `MainWindow`.
 You could do that by defining a class that inherits from `QMainWindow`.
index 72b72fc3faa9df9fba89f367fb497ac92f3933e7..8a69a3c6fa73fbe9127c274b83e002ff8a2b8825 100644 (file)
@@ -34,7 +34,7 @@ names and how they look like.
 
     .. grid-item-card:: Basic Connections
         :class-item: cover-img
-        :link: basictutorial/signal_and_slots.html
+        :link: basictutorial/signals_and_slots.html
         :img-top: basictutorial/signals_slots.png
 
         Signals and Slots
index 2a3bd70799fb6a68f83202b8988436421f609c1d..87fb9766024522baa045446728109ff83a663034 100644 (file)
@@ -26,7 +26,7 @@ the tables. Port these helper functions first. Here is how
 the C++ and Python versions of these functions look like:
 
 C++ version
-------------
+-----------
 
 .. literalinclude:: initdb.h
    :language: c++
@@ -34,7 +34,7 @@ C++ version
    :lines: 9-33
 
 Python version
----------------
+--------------
 
 .. literalinclude:: createdb.py
    :language: python
@@ -45,16 +45,16 @@ Now that the helper functions are in place, port ``initDb``.
 Here is how the C++ and Python versions of this function
 looks like:
 
-C++ version
-------------
+C++ version (initDb)
+--------------------
 
 .. literalinclude:: initdb.h
    :language: c++
    :linenos:
    :lines: 35-112
 
-Python version
----------------
+Python version (init_db)
+------------------------
 
 .. literalinclude:: createdb.py
    :language: python
@@ -77,7 +77,7 @@ test it, add the following code to ``main.py`` and run it:
 
 Use the following command from the prompt to run:
 
-.. code-block::
+.. code-block:: bash
 
     python main.py
 
index fa6ef1116e7d26c5b1c34dc0542e76ada120c246..83ba3357b0fa5370750e7242394b2b415d2437b2 100644 (file)
@@ -47,16 +47,16 @@ For example, painting stars to represent the rating for
 each book in the table. Here is how the reimplemented
 code looks like:
 
-C++ version
-------------
+C++ version (bookdelegate)
+--------------------------
 
 .. literalinclude:: bookdelegate.cpp
    :language: c++
    :linenos:
    :lines: 22-
 
-Python version
----------------
+Python version (bookdelegate)
+-----------------------------
 
 .. literalinclude:: bookdelegate.py
    :language: python
index 59ce6c9f1840defdc0fa17dc17c9eb36f92ebad4..98d4d39828666ade31961fcd8dddf0bb58c035b7 100644 (file)
@@ -21,9 +21,9 @@ and add the following imports to it:
 To generate this Python code, run the following command on the
 prompt:
 
-.. code-block::
+.. code-block:: bash
 
-   pyside6-uic bookwindow.ui -o ui_bookwindow.py
+    pyside6-uic bookwindow.ui -o ui_bookwindow.py
 
 Try porting the remaining code now. To begin with, here is
 how both the versions of the constructor code looks:
@@ -101,9 +101,9 @@ image only.
 Now, run the ``pyside6-rcc`` tool on the ``books.qrc`` file
 to generate ``rc_books.py``.
 
-.. code-block::
+.. code-block:: bash
 
-   pyside6-rcc books.qrc -o rc_books.py
+    pyside6-rcc books.qrc -o rc_books.py
 
 Once you have the Python script generated, make the
 following changes to ``bookdelegate.py`` and ``main.py``:
index c664f2e463bccbae7ca0f83c64b59a6a5c2fc3d5..eee3f807e01e09a2d63c943436988db0bf9a68cc 100644 (file)
@@ -144,7 +144,6 @@ remaining space that is left after accommodating the Pane.
 .. _attached properties: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html
 .. _Layout.fillWidth: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillWidth-attached-prop
 .. _Layout.fillHeight: https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#fillHeight-attached-prop
-.. _ListView: https://doc.qt.io/qt-5/qml-qtquick-listview.html
 .. _Qt Quick QML Types: https://doc.qt.io/qt-5/qtquick-qmlmodule.html
 
 Let's look at the ``Listview`` in detail:
index 82a78a06b4b2981a7ab757eaf258a2efacda954b..919c9ff38c69087e0af7652bc9b665140eea0c77 100644 (file)
@@ -88,6 +88,12 @@ Conferences
 .. grid:: 1 3 3 3
     :gutter: 2
 
+    .. grid-item-card:: PyConES 2023
+        :img-top: https://img.youtube.com/vi/XuqdTvisqkQ/mqdefault.jpg
+        :link: https://www.youtube.com/embed/XuqdTvisqkQ
+
+        Asynchronous programming with asyncio and Qt
+
     .. grid-item-card:: QtWS 2022
         :img-top: https://img.youtube.com/vi/8wcdN1Iw1Uk/mqdefault.jpg
         :link: https://www.youtube.com/embed/8wcdN1Iw1Uk
index 01a0a66b0d7085932c9e811dd62b4cdce106cb8d..e9fd0d56ecee92374fa886e7727783a400bf0bf0 100644 (file)
@@ -5,7 +5,10 @@ project(libpyside)
 
 set(libpyside_libraries Qt::Core Qt::CorePrivate)
 
+set(CMAKE_AUTOMOC ON)
+
 set(libpyside_HEADERS # installed below
+    pysideqslotobject_p.h
     class_property.h
     dynamicqmetaobject.h
     feature_select.h
@@ -41,6 +44,7 @@ set(libpyside_HEADERS # installed below
 )
 
 set(libpyside_SRC
+    pysideqslotobject_p.cpp
     class_property.cpp
     dynamicqmetaobject.cpp
     feature_select.cpp
index ddbd082e842d88aa848d58b8d264f160c94e4d5f..09385645d2b76537bb5ffd6cda2a2185fa4a6c41 100644 (file)
@@ -10,7 +10,6 @@
 #include <autodecref.h>
 #include <gilstate.h>
 
-#include <QtCore/qhashfunctions.h>
 #include <QtCore/QMetaMethod>
 #include <QtCore/QSet>
 #include <QtCore/QDebug>
 namespace PySide
 {
 
-size_t qHash(const GlobalReceiverKey &k, size_t seed)
-{
-    QtPrivate::QHashCombine hash;
-    seed = hash(seed, k.object);
-    seed = hash(seed, k.method);
-    return seed;
-}
-
 class DynamicSlotDataV2
 {
     Q_DISABLE_COPY_MOVE(DynamicSlotDataV2)
index 0e78b9fee7053147305331cbc9d3721beadaabbb..24911c3d04e29b81b8581609cccd6a1a54932bd8 100644 (file)
@@ -9,6 +9,7 @@
 #include "dynamicqmetaobject.h"
 
 #include <QtCore/QByteArray>
+#include <QtCore/QHashFunctions>
 #include <QtCore/QObject>
 #include <QtCore/QPointer>
 #include <QtCore/QMap>
@@ -27,6 +28,11 @@ struct GlobalReceiverKey
 {
     const PyObject *object;
     const PyObject *method;
+
+    friend constexpr size_t qHash(GlobalReceiverKey k, size_t seed = 0) noexcept
+    {
+        return qHashMulti(seed, k.object, k.method);
+    }
 };
 
 inline bool operator==(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
@@ -39,8 +45,6 @@ inline bool operator!=(const GlobalReceiverKey &k1, const GlobalReceiverKey &k2)
     return k1.object != k2.object || k1.method != k2.method;
 }
 
-size_t qHash(const GlobalReceiverKey &k, size_t seed = 0);
-
 /// A class used to link C++ Signals to non C++ slots (Python callbacks) by
 /// providing fake slots for QObject::connect().
 /// It keeps a Python callback and the list of QObject senders. It is stored
diff --git a/sources/pyside6/libpyside/pysideqslotobject_p.cpp b/sources/pyside6/libpyside/pysideqslotobject_p.cpp
new file mode 100644 (file)
index 0000000..914be89
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pysideqslotobject_p.h"
+
+#include <autodecref.h>
+#include <gilstate.h>
+
+namespace PySide
+{
+
+void PySideQSlotObject::impl(int which, QSlotObjectBase *this_, QObject *receiver,
+                             void **args, bool *ret)
+{
+    auto self = static_cast<PySideQSlotObject *>(this_);
+    switch (which) {
+    case Destroy:
+        delete self;
+        break;
+    case Call:
+        {
+            Shiboken::GilState state;
+            Shiboken::AutoDecRef arglist(PyTuple_New(0));
+            Shiboken::AutoDecRef ret(PyObject_CallObject(self->callable, arglist));
+            break;
+        }
+    case Compare:
+    case NumOperations:
+        Q_UNUSED(receiver);
+        Q_UNUSED(args);
+        Q_UNUSED(ret);
+        break;
+    }
+}
+
+} // namespace PySide
diff --git a/sources/pyside6/libpyside/pysideqslotobject_p.h b/sources/pyside6/libpyside/pysideqslotobject_p.h
new file mode 100644 (file)
index 0000000..d7d2585
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef PYSIDEQSLOTOBJECT_P_H
+#define PYSIDEQSLOTOBJECT_P_H
+
+#include "pysidemacros.h"
+#include <sbkpython.h>
+
+#include <QtCore/QObject>
+#include <QtCore/qobjectdefs.h>
+
+namespace PySide
+{
+
+class PySideQSlotObject : public QtPrivate::QSlotObjectBase
+{
+    PyObject *callable;
+
+    static void impl(int which, QSlotObjectBase *this_, QObject *receiver, void **args, bool *ret);
+
+public:
+    PySideQSlotObject(PyObject *callable) : QtPrivate::QSlotObjectBase(&impl), callable(callable)
+    {
+        Py_INCREF(callable);
+    }
+
+    ~PySideQSlotObject()
+    {
+        auto gstate = PyGILState_Ensure();
+        Py_DECREF(callable);
+        PyGILState_Release(gstate);
+    }
+};
+
+
+} // namespace PySide
+
+#endif // PYSIDEQSLOTOBJECT_P_H
index 4653e3940b856b2f73ce020300fcec27b6d316e7..f8c23b9128242b821f5c14ccd492ff0d4426720d 100644 (file)
@@ -103,24 +103,9 @@ static std::optional<QByteArrayList> parseArgumentNames(PyObject *argArguments)
 }
 
 namespace PySide::Signal {
-    //aux
-    class SignalSignature {
-    public:
-        SignalSignature() = default;
-        explicit SignalSignature(QByteArray parameterTypes) :
-            m_parameterTypes(std::move(parameterTypes)) {}
-        explicit SignalSignature(QByteArray parameterTypes, QMetaMethod::Attributes attributes) :
-            m_parameterTypes(std::move(parameterTypes)),
-            m_attributes(attributes) {}
-
-        QByteArray m_parameterTypes;
-        QMetaMethod::Attributes m_attributes = QMetaMethod::Compatibility;
-    };
-
     static QByteArray buildSignature(const QByteArray &, const QByteArray &);
-    static void appendSignature(PySideSignal *, const SignalSignature &);
     static void instanceInitialize(PySideSignalInstance *, PyObject *, PySideSignal *, PyObject *, int);
-    static QByteArray parseSignature(PyObject *);
+    static PySideSignalData::Signature parseSignature(PyObject *);
     static PyObject *buildQtCompatible(const QByteArray &);
 } // PySide::Signal
 
@@ -292,19 +277,12 @@ static int signalTpInit(PyObject *obSelf, PyObject *args, PyObject *kwds)
         PyObject *arg = PyTuple_GET_ITEM(args, i);
         if (PySequence_Check(arg) && !Shiboken::String::check(arg) && !PyEnumMeta_Check(arg)) {
             tupledArgs = true;
-            const auto sig = PySide::Signal::parseSignature(arg);
-            PySide::Signal::appendSignature(
-                        self,
-                        PySide::Signal::SignalSignature(sig));
+            self->data->signatures.append(PySide::Signal::parseSignature(arg));
         }
     }
 
-    if (!tupledArgs) {
-        const auto sig = PySide::Signal::parseSignature(args);
-        PySide::Signal::appendSignature(
-                    self,
-                    PySide::Signal::SignalSignature(sig));
-    }
+    if (!tupledArgs)
+        self->data->signatures.append(PySide::Signal::parseSignature(args));
 
     return 0;
 }
@@ -328,7 +306,7 @@ static PyObject *signalGetItem(PyObject *obSelf, PyObject *key)
     auto self = reinterpret_cast<PySideSignal *>(obSelf);
     QByteArray sigKey;
     if (key) {
-        sigKey = PySide::Signal::parseSignature(key);
+        sigKey = PySide::Signal::parseSignature(key).signature;
     } else {
         sigKey = self->data == nullptr || self->data->signatures.isEmpty()
             ? PySide::Signal::voidType() : self->data->signatures.constFirst().signature;
@@ -648,7 +626,7 @@ static PyObject *signalInstanceGetItem(PyObject *self, PyObject *key)
 {
     auto *firstSignal = reinterpret_cast<PySideSignalInstance *>(self);
     const auto &sigName = firstSignal->d->signalName;
-    const auto sigKey = PySide::Signal::parseSignature(key);
+    const auto sigKey = PySide::Signal::parseSignature(key).signature;
     const auto sig = PySide::Signal::buildSignature(sigName, sigKey);
     for (auto *data = firstSignal; data != nullptr; data = data->d->next) {
         if (data->d->signature == sig) {
@@ -984,27 +962,24 @@ static QByteArray buildSignature(const QByteArray &name, const QByteArray &signa
     return QMetaObject::normalizedSignature(name + '(' + signature + ')');
 }
 
-static QByteArray parseSignature(PyObject *args)
+static PySideSignalData::Signature parseSignature(PyObject *args)
 {
-    if (args && (Shiboken::String::check(args) || !PyTuple_Check(args)))
-        return getTypeName(args);
+    PySideSignalData::Signature result{{}, QMetaMethod::Compatibility};
+    if (args && (Shiboken::String::check(args) || !PyTuple_Check(args))) {
+        result.signature = getTypeName(args);
+        return result;
+    }
 
-    QByteArray signature;
     for (Py_ssize_t i = 0, i_max = PySequence_Size(args); i < i_max; i++) {
         Shiboken::AutoDecRef arg(PySequence_GetItem(args, i));
         const auto typeName = getTypeName(arg);
         if (!typeName.isEmpty()) {
-            if (!signature.isEmpty())
-                signature += ',';
-            signature += typeName;
+            if (!result.signature.isEmpty())
+                result.signature += ',';
+            result.signature += typeName;
         }
     }
-    return signature;
-}
-
-static void appendSignature(PySideSignal *self, const SignalSignature &signature)
-{
-    self->data->signatures.append({signature.m_parameterTypes, signature.m_attributes});
+    return result;
 }
 
 static void sourceGone(void *data)
@@ -1110,26 +1085,6 @@ PySideSignalInstance *newObjectFromMethod(PyObject *source, const QList<QMetaMet
     return root;
 }
 
-template<typename T>
-static typename T::value_type join(T t, const char *sep)
-{
-    typename T::value_type res;
-    if (t.isEmpty())
-        return res;
-
-    typename T::const_iterator it = t.begin();
-    typename T::const_iterator end = t.end();
-    res += *it;
-    ++it;
-
-    while (it != end) {
-        res += sep;
-        res += *it;
-        ++it;
-    }
-    return res;
-}
-
 static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalName, PySideSignal *signal)
 {
     Shiboken::AutoDecRef tpDict(PepType_GetDict(wrapperType));
@@ -1143,9 +1098,10 @@ static void _addSignalToWrapper(PyTypeObject *wrapperType, const char *signalNam
 }
 
 // This function is used by qStableSort to promote empty signatures
-static bool compareSignals(const SignalSignature &sig1, const SignalSignature &)
+static bool compareSignals(const PySideSignalData::Signature &sig1,
+                           const PySideSignalData::Signature &sig2)
 {
-    return sig1.m_parameterTypes.isEmpty();
+    return sig1.signature.isEmpty() && !sig2.signature.isEmpty();
 }
 
 static PyObject *buildQtCompatible(const QByteArray &signature)
@@ -1156,7 +1112,8 @@ static PyObject *buildQtCompatible(const QByteArray &signature)
 
 void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
 {
-    using SignalSigMap = QHash<QByteArray, QList<SignalSignature> >;
+    using Signature = PySideSignalData::Signature;
+    using SignalSigMap = QHash<QByteArray, QList<Signature>>;
     SignalSigMap signalsFound;
     for (int i = metaObject->methodOffset(), max = metaObject->methodCount(); i < max; ++i) {
         QMetaMethod method = metaObject->method(i);
@@ -1164,10 +1121,9 @@ void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
         if (method.methodType() == QMetaMethod::Signal) {
             QByteArray methodName(method.methodSignature());
             methodName.chop(methodName.size() - methodName.indexOf('('));
-            SignalSignature signature;
-            signature.m_parameterTypes = join(method.parameterTypes(), ",");
+            Signature signature{method.parameterTypes().join(','), {}};
             if (method.attributes() & QMetaMethod::Cloned)
-                signature.m_attributes = QMetaMethod::Cloned;
+                signature.attributes = QMetaMethod::Cloned;
             signalsFound[methodName] << signature;
         }
     }
@@ -1181,12 +1137,9 @@ void registerSignals(PyTypeObject *pyObj, const QMetaObject *metaObject)
         self->homonymousMethod = nullptr;
 
         // Empty signatures comes first! So they will be the default signal signature
-        std::stable_sort(it.value().begin(), it.value().end(), &compareSignals);
-        const auto endJ = it.value().cend();
-        for (auto j = it.value().cbegin(); j != endJ; ++j) {
-            const SignalSignature &sig = *j;
-            appendSignature(self, sig);
-        }
+        self->data->signatures = it.value();
+        std::stable_sort(self->data->signatures.begin(),
+                         self->data->signatures.end(), &compareSignals);
 
         _addSignalToWrapper(pyObj, it.key(), self);
         Py_DECREF(reinterpret_cast<PyObject *>(self));
index 37ccb58d60f2bfe2229a3988d45c5807c7e8a434..8a0feea4cd83d1851d06929cea552aa81aabb135 100644 (file)
@@ -13,8 +13,8 @@ struct PySideSignalData
 {
     struct Signature
     {
-        QByteArray signature;
-        int attributes;
+        QByteArray signature; // ','-separated list of parameter types
+        unsigned short attributes;
     };
 
     QByteArray signalName;
@@ -39,10 +39,10 @@ struct PySideSignalInstancePrivate
 {
     QByteArray signalName;
     QByteArray signature;
-    int attributes = 0;
     PyObject *source = nullptr;
     PyObject *homonymousMethod = nullptr;
     PySideSignalInstance *next = nullptr;
+    unsigned short attributes = 0;
 };
 
 namespace PySide::Signal {
index 15b71766cb4fadc129de7b56e7137a0dfb8f3644..c47c96eb88571598c96d9ccb9c1e8f3987688e42 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "qobjectconnect.h"
 #include "pysideqobject.h"
+#include "pysideqslotobject_p.h"
 #include "pysidesignal.h"
 #include "pysideutils.h"
 #include "signalmanager.h"
@@ -15,6 +16,8 @@
 #include <QtCore/QMetaMethod>
 #include <QtCore/QObject>
 
+#include <QtCore/private/qobject_p.h>
+
 #include <string_view>
 
 static bool isMethodDecorator(PyObject *method, bool is_pymethod, PyObject *self)
@@ -259,6 +262,46 @@ QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *sign
     return connection;
 }
 
+QMetaObject::Connection qobjectConnectCallback(QObject *source, const char *signal, QObject *context,
+                                               PyObject *callback, Qt::ConnectionType type)
+{
+    if (!signal || !PySide::Signal::checkQtSignal(signal))
+        return {};
+
+    const int signalIndex =
+        PySide::SignalManager::registerMetaMethodGetIndex(source, signal + 1,
+                                                          QMetaMethod::Signal);
+    if (signalIndex == -1)
+        return {};
+
+    // Extract receiver from callback
+    const GetReceiverResult receiver = getReceiver(source, signal + 1, callback);
+    if (receiver.receiver == nullptr && receiver.self == nullptr)
+        return {};
+
+    PySide::SignalManager &signalManager = PySide::SignalManager::instance();
+
+    PySideQSlotObject *slotObject = new PySideQSlotObject(callback);
+
+    QMetaObject::Connection connection{};
+    Py_BEGIN_ALLOW_THREADS // PYSIDE-2367, prevent threading deadlocks with connectNotify()
+    connection = QObjectPrivate::connect(source, signalIndex, context, slotObject, type);
+    Py_END_ALLOW_THREADS
+    if (!connection) {
+        if (receiver.usingGlobalReceiver)
+            signalManager.releaseGlobalReceiver(source, receiver.receiver);
+        return {};
+    }
+
+    Q_ASSERT(receiver.receiver);
+    if (receiver.usingGlobalReceiver)
+        signalManager.notifyGlobalReceiver(receiver.receiver);
+
+    const QMetaMethod signalMethod = receiver.receiver->metaObject()->method(signalIndex);
+    static_cast<FriendlyQObject *>(source)->connectNotify(signalMethod);
+    return connection;
+}
+
 bool qobjectDisconnectCallback(QObject *source, const char *signal, PyObject *callback)
 {
     if (!PySide::Signal::checkQtSignal(signal))
index 70b862233e6618e1622fcf5606d5edc6259a32a1..c99b8006ed216398cdaeceafcf851a954dd1336f 100644 (file)
@@ -33,6 +33,11 @@ PYSIDE_API QMetaObject::Connection
     qobjectConnectCallback(QObject *source, const char *signal,
                            PyObject *callback, Qt::ConnectionType type);
 
+/// Helpers for QObject::connect(): Make a connection to a Python callback and a context object
+PYSIDE_API QMetaObject::Connection
+    qobjectConnectCallback(QObject *source, const char *signal, QObject *context,
+                           PyObject *callback, Qt::ConnectionType type);
+
 /// Helpers for QObject::disconnect(): Disconnect a Python callback
 PYSIDE_API bool qobjectDisconnectCallback(QObject *source, const char *signal,
                                           PyObject *callback);
index 1e6c7d28d539f95e62b6eace6d7b18680a7830dd..8d0e8065031ee0fdd89525c4f8a38d1ea5caea90 100644 (file)
@@ -7,11 +7,14 @@
 #include "pysideqmlmacros.h"
 
 #include <sbkpython.h>
+#include <QtCore/qtconfigmacros.h>
 
+QT_BEGIN_NAMESPACE
 namespace QQmlPrivate
 {
 struct RegisterType;
 }
+QT_END_NAMESPACE
 
 namespace PySide::Qml
 {
@@ -80,7 +83,7 @@ PYSIDEQML_API PyObject *qmlSingletonMacro(PyObject *pyObj);
 // Used by QtQuick module to fill the QQmlPrivate::RegisterType::parserStatusCast,
 // valueSourceCast and valueInterceptorCast fields with the correct values.
 using QuickRegisterItemFunction =
-    bool (*)(PyObject *pyObj, QQmlPrivate::RegisterType *);
+    bool (*)(PyObject *pyObj, QT_PREPEND_NAMESPACE(QQmlPrivate::RegisterType) *);
 
 PYSIDEQML_API QuickRegisterItemFunction getQuickRegisterItemFunction();
 PYSIDEQML_API void setQuickRegisterItemFunction(QuickRegisterItemFunction function);
index 6491251b9e3f499cc346537fbfa9e57f1f3fdabf..ccff635a91527e6c8c0b1d08fcd27cbf3f7be607 100644 (file)
@@ -1,6 +1,7 @@
 // Copyright (C) 2021 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
+#undef slots
 #include <Python.h> // Include before Qt headers due to 'slots' macro definition
 
 #include "designercustomwidgets.h"
index e18b62543f5bea04afb10b5477bb703081c3634c..c581a8d0f9702d742a18f3f615bb9888195601ca 100644 (file)
@@ -54,6 +54,10 @@ add_subdirectory(support)
 add_subdirectory(tools/metaobjectdump)
 add_subdirectory(tools/pyside6-deploy)
 
+if(UNIX AND NOT APPLE)
+    add_subdirectory(tools/pyside6-android-deploy)
+endif()
+
 if (NOT DISABLE_QtQuick)
     add_subdirectory(tools/pyside6-qml)
 endif()
index 953483818037fa8c7f1c8badf01b567bd53466a7..f3c971285af56385359b971afe9ccff966c6d33d 100644 (file)
@@ -12,7 +12,7 @@ from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
 class QAsyncioTestCase(unittest.TestCase):
     async def sleep(self, output):
         output += "Hello"
-        await asyncio.sleep(1)
+        await asyncio.sleep(0.2)
         output += "World"
 
     async def gather(self, output):
diff --git a/sources/pyside6/tests/QtAsyncio/qasyncio_test_cancel_task.py b/sources/pyside6/tests/QtAsyncio/qasyncio_test_cancel_task.py
new file mode 100644 (file)
index 0000000..7ef2bb9
--- /dev/null
@@ -0,0 +1,46 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+'''Test cases for QtAsyncio'''
+
+import asyncio
+import unittest
+
+import PySide6.QtAsyncio as QtAsyncio
+
+
+class QAsyncioTestCaseCancelTask(unittest.TestCase):
+    # Taken from https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.cancel
+
+    async def cancel_me(self, output):
+        output += "(1) cancel_me(): before sleep"
+
+        try:
+            await asyncio.sleep(10)
+        except asyncio.CancelledError:
+            output += "(2) cancel_me(): cancel sleep"
+            raise
+        finally:
+            output += "(3) cancel_me(): after sleep"
+
+    async def main(self, output):
+        task = asyncio.create_task(self.cancel_me(output))
+        await asyncio.sleep(0.1)
+        task.cancel()
+        try:
+            await task
+        except asyncio.CancelledError:
+            output += "(4) main(): cancel_me is cancelled now"
+
+    def test_await_tasks(self):
+        output_expected = []
+        output_real = []
+
+        asyncio.run(self.main(output_expected))
+        QtAsyncio.run(self.main(output_real), keep_running=False)
+
+        self.assertEqual(output_real, output_expected)
+
+
+if __name__ == '__main__':
+    unittest.main()
index f45b51a71dacb75cddb06d5428e9a28d1bbdff33..a0a949720e5c7d95affd5565d9bf4870f5fcc28b 100644 (file)
@@ -22,8 +22,8 @@ class QAsyncioTestCaseChain(unittest.TestCase):
         return result
 
     async def chain(self, output, n):
-        link1 = await self.link(output, n, 1)
-        link2 = await self.link(output, n, 2)
+        link1 = await self.link(output, n, 0.2)
+        link2 = await self.link(output, n, 0.5)
         output += f"chain {n}: {link1} -> {link2} "
 
     async def gather(self, output, *args):
index f343aa73b3c1a355c2360c9aa5c37e89346070d4..25e680b3979e3eba87074c60382c40290806b66f 100644 (file)
@@ -27,7 +27,8 @@ class QAsyncioTestCaseExecutor(unittest.TestCase):
     async def run_asyncio_executor(self):
         main_thread = QThread.currentThread()
         with ThreadPoolExecutor(max_workers=2) as executor:
-            result = await asyncio.get_running_loop().run_in_executor(executor, self.blocking_function)
+            result = await asyncio.get_running_loop().run_in_executor(
+                executor, self.blocking_function)
 
             # Assert that we are back to the main thread.
             self.assertEqual(QThread.currentThread(), main_thread)
index 38827b0f7b4d8f3e2b409db7751a7e5dfc5f3633..0bd98c361f285811e7ec2e12816b1fedc4aad579 100644 (file)
@@ -14,24 +14,26 @@ from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
 class QAsyncioTestCaseQueues(unittest.TestCase):
 
     async def produce(self, output, queue):
-        for _ in range(random.randint(0, 3)):
-            await asyncio.sleep(random.randint(0, 2))
+        for _ in range(random.randint(0, 2)):
+            await asyncio.sleep(random.random())
             await queue.put(self.i)
             output += f"{self.i} added to queue\n"
             self.i += 1
 
     async def consume(self, output, queue):
         while True:
-            await asyncio.sleep(random.randint(0, 2))
+            await asyncio.sleep(random.random())
             i = await queue.get()
             output += f"{i} pulled from queue\n"
             queue.task_done()
 
     async def main(self, output1, output2, num_producers, num_consumers):
         self.i = 0
-        queue = asyncio.Queue()  # type: asyncio.Queue
-        producers = [asyncio.create_task(self.produce(output1, queue)) for _ in range(num_producers)]
-        consumers = [asyncio.create_task(self.consume(output2, queue)) for _ in range(num_consumers)]
+        queue = asyncio.Queue()
+        producers = [
+            asyncio.create_task(self.produce(output1, queue)) for _ in range(num_producers)]
+        consumers = [
+            asyncio.create_task(self.consume(output2, queue)) for _ in range(num_consumers)]
         await asyncio.gather(*producers)
         await queue.join()
         for consumer in consumers:
diff --git a/sources/pyside6/tests/QtAsyncio/qasyncio_test_threadsafe.py b/sources/pyside6/tests/QtAsyncio/qasyncio_test_threadsafe.py
new file mode 100644 (file)
index 0000000..5b52db2
--- /dev/null
@@ -0,0 +1,58 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+'''Test cases for QtAsyncio'''
+
+import unittest
+import asyncio
+import threading
+import time
+
+from PySide6.QtAsyncio import QAsyncioEventLoopPolicy
+
+
+class QAsyncioTestCaseThreadsafe(unittest.TestCase):
+
+    def setUp(self) -> None:
+        super().setUp()
+        asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
+        self.loop_event = asyncio.Event()
+
+    def thread_target(self, is_threadsafe):
+        time.sleep(1)
+        if is_threadsafe:
+            # call_soon_threadsafe() wakes the loop that is in another thread, so the
+            # loop checks the event and will not hang.
+            asyncio.get_event_loop().call_soon_threadsafe(self.loop_event.set)
+        else:
+            # call_soon() does not wake the loop that is in another thread, and so the
+            # loop keeps waiting without checking the event and will hang.
+            asyncio.get_event_loop().call_soon(self.loop_event.set)
+
+    async def coro(self, is_threadsafe):
+        thread = threading.Thread(target=self.thread_target, args=(is_threadsafe,))
+        thread.start()
+
+        task = asyncio.create_task(self.loop_event.wait())
+
+        # The timeout is necessary because the loop will hang for the non-threadsafe case.
+        done, pending = await asyncio.wait([task], timeout=2)
+
+        thread.join()
+
+        if is_threadsafe:
+            self.assertEqual(len(done), 1)
+            self.assertEqual(len(pending), 0)
+        else:
+            self.assertEqual(len(done), 0)
+            self.assertEqual(len(pending), 1)
+
+    def test_not_threadsafe(self):
+        asyncio.run(self.coro(False))
+
+    def test_threadsafe(self):
+        asyncio.run(self.coro(True))
+
+
+if __name__ == '__main__':
+    unittest.main()
index 60ec2f06466a649434b1459b7801a8db0aac5d72..07a1266444d6523b202dd66650b7e020304bb4d7 100644 (file)
@@ -33,7 +33,7 @@ class QAsyncioTestCaseTime(unittest.TestCase):
         asyncio.set_event_loop_policy(QAsyncioEventLoopPolicy())
         loop = asyncio.new_event_loop()
 
-        end_time = loop.time() + 5.0
+        end_time = loop.time() + 3.0
         loop.call_soon(self.display_date, end_time, loop)
 
         try:
index b09cc12b2294765c66a6af34181468f1902e0a6d..21801106f19ce3fd8e7bed59d2d7354e0d8ed18d 100644 (file)
@@ -15,9 +15,11 @@ init_test_paths(False)
 
 from PySide6.QtBluetooth import QBluetoothLocalDevice
 
+
 class QBluetoothLocalDeviceTest(unittest.TestCase):
     def testInitialization(self):
         device = QBluetoothLocalDevice()
 
+
 if __name__ == '__main__':
     unittest.main()
index 52293b808a4b920b34ea7757c0002bf91b274f11..4e270a441a156131e90469f90be21d31b495c686 100644 (file)
@@ -26,7 +26,7 @@ def _cleanup():
 
 def _checkCleanup():
     global callCleanup
-    assert(callCleanup)
+    assert (callCleanup)
 
 
 app = QCoreApplication([])
index 30d141acf1bad9a6d26c0a6278c8ed64ccafdf72..0b0a0b4b6b658d912756ed4e446ca521de181162 100644 (file)
@@ -33,7 +33,7 @@ class TestClassInfo(unittest.TestCase):
         self.assertEqual(ci.value(), 'http://www.pyside.org')
 
     def test_dictionary(self):
-        @ClassInfo({'author':'pyside', 'author company':'The Qt Company'})
+        @ClassInfo({'author': 'pyside', 'author company': 'The Qt Company'})
         class MyObject(QObject):
             pass
 
@@ -50,11 +50,11 @@ class TestClassInfo(unittest.TestCase):
         self.assertEqual(ci.value(), 'The Qt Company')
 
     def test_verify_metadata_types(self):
-        valid_dict = { '123': '456' }
+        valid_dict = {'123': '456'}
 
-        invalid_dict_1 = { '123': 456 }
-        invalid_dict_2 = {  123:  456 }
-        invalid_dict_3 = {  123: '456' }
+        invalid_dict_1 = {'123': 456}
+        invalid_dict_2 = {123: 456}
+        invalid_dict_3 = {123: '456'}
 
         ClassInfo(**valid_dict)
 
@@ -88,6 +88,7 @@ class TestClassInfo(unittest.TestCase):
     def test_can_only_be_used_on_qobjects(self):
         def make_info():
             return ClassInfo(author='pyside')
+
         def test_function():
             pass
         self.assertRaises(TypeError, make_info(), test_function)
index 6632fbaf97d7a07d93769cd5c30ec995fc0474c0..c55bcaf92750591b599cb1f5a10b1c6e538e9948 100644 (file)
@@ -28,7 +28,6 @@ class Ui_MainWindow(object):
 
         self.verticalLayout.addWidget(self.pushButton)
 
-
         self.horizontalLayout.addLayout(self.verticalLayout)
 
         MainWindow.setCentralWidget(self.centralwidget)
index fa85d95ef3a88f175a996445c508810498561a31..a808f0c3d91a75cdf6186cc63d37b569df1ff49f 100644 (file)
@@ -17,6 +17,7 @@ from PySide6.QtCore import (QLoggingCategory, QtMsgType, qCDebug, qCWarning, qCI
 
 param = None
 
+
 def handler(msgt, ctx, msg):
     global param
     param = ctx.category + ": " + msg.strip()
@@ -70,7 +71,6 @@ class TestQLoggingCategory(unittest.TestCase):
         qCWarning(self.criticalCategory, f"devices: {self.no_devices}")
         self.assertEqual(param, "warning.log: devices: 2")
 
-
     def test_qCritical(self):
         qCCritical(self.defaultCategory, "no device")
         self.assertEqual(param, "default: no device")
index 4b6b05664581b9893e4d6bd35e06a9ed4603b9e0..0f51ace6b690a49b61cf02328d8de84c72d150a1 100644 (file)
@@ -30,6 +30,7 @@ There is much more to come.
 
 MethodDescriptorType = type(str.split)
 
+
 def xprint(*args, **kw):
     if "-v" in sys.argv:
         print(*args, **kw)
index 6c7606dd2613100c9bc4a3e1d20adba109687876..312c9e5c7eba4204987f9e8f95bb3b59611ac273 100644 (file)
@@ -15,7 +15,7 @@ from init_paths import init_test_paths
 init_test_paths(False)
 
 from PySide6.QtCore import (QByteArray, QCborStreamReader, QCborStreamWriter,
-    QCborTag, QCborValue)
+                            QCborTag, QCborValue)
 
 
 class TestCbor(unittest.TestCase):
index 160426b013fc795459962c53902d572443b931c1..6265f77b10bf45b9f47f0fcc2af0c11d7e683075 100644 (file)
@@ -105,10 +105,9 @@ class ParentCase(unittest.TestCase):
         for i, child in enumerate(children):
             self.assertEqual(child, parent.findChild(QObject, f'object{i}'))
 
-
     def testFindChildOptions(self):
         parent = QObject()
-        child  = QObject(parent)
+        child = QObject(parent)
         nested_child_name = 'nestedChild'
         nested_child = QObject(child)
         nested_child.setObjectName(nested_child_name)
index 67285adcc03bd8fec3672be3184b3e7c3aed7f6e..1cdd2c785f0ccf5b2c225b3b8ddd4c3ba6759007 100644 (file)
@@ -31,7 +31,7 @@ class ResourcesUsage(unittest.TestCase):
             if carriage_return != -1:
                 orig.remove(carriage_return, 1)
 
-        f = QFile(':/quote.txt')  #|QIODevice.Text
+        f = QFile(':/quote.txt')  # |QIODevice.Text
         self.assertTrue(f.open(QIODevice.ReadOnly), f.errorString())
         copy = f.readAll()
         f.close()
index 1392281facfe51a1af50459cfbd34eb2d105739a..9718a242745ea67de0d44fc7e93d476f47373aa3 100644 (file)
@@ -85,6 +85,17 @@ class TestSingleShot(UsesQApplication):
         self.assertTrue(thread.called)
         self.assertEqual(self.qthread, thread.qthread)
 
+    def testSingleShotWithContextZero(self):
+        thread = ThreadForContext()
+        thread.start()
+        thread.context.moveToThread(thread)
+        QTimer.singleShot(0, thread.context, self.callback)
+        self.app.exec()
+        thread.wait()
+        self.assertTrue(self.called)
+        self.assertTrue(thread.called)
+        self.assertEqual(self.qthread, thread.qthread)
+
 
 class SigEmitter(QObject):
 
index 4178c7a109e5e97b6693a74e4d3ef5d2fcc94184..82087ab9a38bacd1db7547287da8be664e7a7978 100644 (file)
@@ -60,7 +60,7 @@ class FeatureTest(unittest.TestCase):
             window.modal
 
         from __feature__ import snake_case, true_property
-        #PYSIDE-1548: Make sure that another import does not clear the features.
+        # PYSIDE-1548: Make sure that another import does not clear the features.
         import sys
 
         self.assertTrue(isinstance(QWidget.modal, property))
index eb089294bb60041c42b182f24548891a5a06f87f..98ada6d47fd026414ec1dcd95a7cb8b7a3744dda 100644 (file)
@@ -82,8 +82,8 @@ class QtDataVisualizationTestCase(UsesQApplication):
         self.assertTrue(data_proxy.rowCount(), 4)
 
     def testDefaultSurfaceFormat(self):
-         format = qDefaultSurfaceFormat(True)
-         print(format)
+        format = qDefaultSurfaceFormat(True)
+        print(format)
 
     def testQValue3DAxisFormatter(self):
         """PYSIDE-2025: Test the added setters of QValue3DAxisFormatter."""
index 336d58f8edd2982c044ca957f852134bc67ba531..bbd558f10e616511bdf5acec811e0293de493631 100644 (file)
@@ -41,7 +41,7 @@ class QColorGetTest(unittest.TestCase):
 
     def testGetCmykF(self):  # not supported by colorsys
         for x, y in zip(self.color.getCmykF(), (170 / 255.0, 85 / 255.0, 0, 195 / 255.0, 80 / 255.0)):
-            self.assertTrue(x - y < 1/10000.0)
+            self.assertTrue(x - y < 1 / 10000.0)
 
 
 class QColorQRgbConstructor(unittest.TestCase):
index 52ce810d3ae8403723dc643b4db8b468a1bec542..98c4c8f9697ea49ae360cf605cbcc747b3b33873 100644 (file)
@@ -91,8 +91,8 @@ class BoundingRectTest(QFontMetricsTest):
         '''QFontMetrics.boundingRect(QRect, ...) - type error'''
         arg = QRect(0, 0, 100, 200)
         self.assertRaises(TypeError, self.metrics.boundingRect, arg,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextExpandTabs | Qt.AlignLeft,
+                          'PySide by INdT', 20, ['aaaa', 'ase'])
 
 
 class SizeTest(QFontMetricsTest):
@@ -196,8 +196,8 @@ class FSizeTest(QFontMetricsFTest):
     def testTypeError(self):
         '''QFontMetricsF.size - type error'''
         self.assertRaises(TypeError, self.metrics.size,
-                                         Qt.TextExpandTabs | Qt.AlignLeft,
-                                         'PySide by INdT', 20, ['aaaa', 'ase'])
+                          Qt.TextExpandTabs | Qt.AlignLeft,
+                          'PySide by INdT', 20, ['aaaa', 'ase'])
 
 
 class QCharTest(QFontMetricsFTest):
index 80fc5a56b56a4e92184ebfe26ba2407e51145cd8..18ef3d815e3d4659cd1ad937041c35dc1e0dcc92 100644 (file)
@@ -26,6 +26,7 @@ class QIconCtorWithNoneTest(TimedQGuiApplication):
 PIX_PATH = os.fspath(Path(__file__).resolve().parents[2]
                      / "doc/tutorials/basictutorial/icons.png")
 
+
 class QIconAddPixmapTest(TimedQGuiApplication):
     '''PYSIDE-1669: check that addPixmap works'''
 
index e2e8743745d2fe7dd5e4680356c1540a55005de5..3e6bc4c9d99ee25ab427b870b09cea43b61a6d1a 100644 (file)
@@ -16,7 +16,7 @@ from helper.usesqapplication import UsesQApplication
 
 from PySide6.QtCore import QSize, QTimer, Qt
 from PySide6.QtGui import (QColor, QGuiApplication, QImage, QOpenGLContext,
-    QSurfaceFormat)
+                           QSurfaceFormat)
 from PySide6.QtOpenGL import QOpenGLTexture, QOpenGLWindow
 
 
index 299864ae4e1fd32f3e84d8c35a3986a9bf22d427..d81a99d9400bc565620151cbd9d4afb9c85280ab 100644 (file)
@@ -28,6 +28,7 @@ from PySide6.QtQml import QmlElement
 QML_IMPORT_NAME = "test.PythonObject"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 class PythonObject(QObject):
     def __init__(self):
index 7148102cc3ee604f317ce26955aa0919bd9a7a4c..7743ee3fd39d16613ab4b51902620d25263888a3 100644 (file)
@@ -19,6 +19,7 @@ from PySide6.QtQml import QmlElement
 QML_IMPORT_NAME = "test.RotateValue"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 class RotateValue(QObject):
     def __init__(self):
index cc06a4877375dab65cc745fe25636d04426e8b29..eb43973f64f2259986fe9874bca2fb6fe0ac1cc4 100644 (file)
@@ -23,6 +23,6 @@ component = QQmlComponent(engine)
 
 # This should segfault if the QDeclarativeComponent has not QQmlEngine
 file = Path(__file__).resolve().parent / 'foo.qml'
-assert(not file.is_file())
+assert (not file.is_file())
 component.loadUrl(QUrl.fromLocalFile(file))
 
index 7ddc8e93be856e66ef0b38611c91abba30bbd99d..56c1e70f15ae0a264379fb970c70075d153271b2 100644 (file)
@@ -18,6 +18,8 @@ from PySide6.QtQml import QmlElement
 
 QML_IMPORT_NAME = "test.ProxyObject"
 QML_IMPORT_MAJOR_VERSION = 1
+
+
 @QmlElement
 class ProxyObject(QObject):
     def __init__(self):
index 9db6488efce32accd853c3b8559dbb74f1490965..0e7858b6c4b09b3d075f3b64cd201e4047239404 100644 (file)
@@ -29,13 +29,14 @@ from PySide6.QtQml import QmlElement
 QML_IMPORT_NAME = "test.ListModel"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 class ListModel(QAbstractListModel):
     def __init__(self):
         super().__init__()
 
     def roleNames(self):
-        return { Qt.DisplayRole: b'pysideModelData' }
+        return {Qt.DisplayRole: b'pysideModelData'}
 
     def rowCount(self, parent=QModelIndex()):
         return 3
index 1d9b82d1640be7819f5b2ecf1f37ad5318379429..a8bd304ecb3b6910387bc90f278b5544d79ef9a8 100644 (file)
@@ -33,6 +33,7 @@ class MetaA(type):
 class A(object, metaclass=MetaA):
     pass
 
+
 MetaB = type(QQuickPaintedItem)
 B = QQuickPaintedItem
 
index c4853b9ad1325ab7a1394f3bd9c6d1528d69fd09..085e9a68f0e1bf607af3f0f59420adac1c8e85f6 100644 (file)
@@ -41,7 +41,7 @@ class MyClass (QObject):
 class TestBug926 (unittest.TestCase):
     def testIt(self):
         app = QGuiApplication([])
-        qmlRegisterType(MyClass,'Example', 1, 0, 'MyClass')
+        qmlRegisterType(MyClass, 'Example', 1, 0, 'MyClass')
         view = QQuickView()
         file = Path(__file__).resolve().parent / 'bug_926.qml'
         self.assertTrue(file.is_file())
index 745dd7daeec06a485a7b9c42684f9a7da76c2e1a..868c584e2179df8db51dddef250763633f0ea562 100644 (file)
@@ -19,7 +19,7 @@ from PySide6.QtQuick import QQuickView
 
 app = QGuiApplication([])
 file = Path(__file__).resolve().parent / 'bug_995.qml'
-assert(file.is_file())
+assert (file.is_file())
 view = QQuickView(QUrl.fromLocalFile(file))
 view.show()
 view.resize(200, 200)
index f0df1c83dce3e758edc239dd6ed466893c58de5e..2e60aec4feb4ca5d8088d9b43f43eea043ee45fa 100644 (file)
@@ -42,7 +42,8 @@ class TestConnectionWithInvalidSignature(TimedQGuiApplication):
         root = view.rootObject()
         self.assertTrue(root, quickview_errorstring(view))
         button = root.findChild(QObject, "buttonMouseArea")
-        self.assertRaises(TypeError, QObject.connect, [button,SIGNAL('entered()'), self.onButtonFailClicked])
+        self.assertRaises(TypeError, QObject.connect,
+                          [button, SIGNAL('entered()'), self.onButtonFailClicked])
         button.entered.connect(self.onButtonClicked)
         button.entered.emit()
         view.show()
index 5cf5550edb88790b5f491ba09c516060e3b5dcdf..22650966938fb8bbbd04bf3bb2306e5d2dbf810a 100644 (file)
@@ -22,6 +22,7 @@ from PySide6.QtQuick import QQuickView
 
 class MyObject(QObject):
     titleChanged = Signal()
+
     def __init__(self, text, parent=None):
         QObject.__init__(self, parent)
         self._text = text
index f2e318e466a43cae5ea4a861797a24a1ceb2383e..492217fd62beebf8cf7b7f385c62e4eaf2875d4f 100644 (file)
@@ -49,6 +49,7 @@ def singletonQJSValueCallback(engine):
 QML_IMPORT_NAME = "Singletons"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 @QmlSingleton
 class DecoratedSingletonQObject(QObject):
index 304c83ca37c63b1216a83798a14f2745759179f4..f5b0f8bd3d4d6cf5b0aedaaac53ec0467d10839b 100644 (file)
@@ -20,6 +20,7 @@ from PySide6.QtQml import QmlElement
 QML_IMPORT_NAME = "test.Obj"
 QML_IMPORT_MAJOR_VERSION = 1
 
+
 @QmlElement
 class Obj(QObject):
     def __init__(self):
index c247cf9fd236ff353f4c74d996924115f74c8002..52624a2a99fcfef7822938755dc3fca62ee1e76b 100644 (file)
@@ -27,6 +27,6 @@ if __name__ == "__main__":
     app = QApplication([])
 
     file = Path(__file__).resolve().parent / 'bug_1060.ui'
-    assert(file.is_file())
+    assert (file.is_file())
     ui = MyQUiLoader().load(file)
     ui.show()
index ce040f4fb0d9ff9bd83f2ef573b569bf777430b0..06a6b72a4e0655c1c728abac059239cf9b2b2d03 100644 (file)
@@ -21,7 +21,7 @@ class View_1(QWidget):
         super().__init__()
         loader = QUiLoader()
         file = Path(__file__).resolve().parent / 'bug_552.ui'
-        assert(file.is_file())
+        assert (file.is_file())
         widget = loader.load(os.fspath(file), self)
         self.children = []
         for child in widget.findChildren(QObject, None):
index b87ca1293bf6b52a87833343a81dec9ec7e630df..3dae7afb7478eadf077d47b53f07fd7107c8497f 100644 (file)
@@ -18,7 +18,7 @@ from PySide6.QtWidgets import QApplication, QWidget
 app = QApplication([])
 loader = QUiLoader()
 file = Path(__file__).resolve().parent / 'bug_552.ui'
-assert(file.is_file())
+assert (file.is_file())
 file = QFile(file)
 w = QWidget()
 # An exception can't be thrown
index 69f57b03e8bc311d184e1594d52e7351490d9e95..2f8dfab62d3f43411de1403be72d4cd6d2b1718c 100644 (file)
@@ -23,7 +23,7 @@ class Gui_Qt(QMainWindow):
 
         # this used to cause a segfault because the old inject code used to destroy the parent layout
         file = Path(__file__).resolve().parent / 'bug_958.ui'
-        assert(file.is_file())
+        assert (file.is_file())
         self._cw = lLoader.load(file, self)
 
         self.setCentralWidget(self._cw)
index 6e99fbd05bf97288218e43f6a8d41b1f29d60ca1..79dfa49fca6a69caee606854e84735e8152ec5ee 100644 (file)
@@ -27,7 +27,7 @@ class BugTest(UsesQApplication):
         treeWidget.insertTopLevelItems(0, items)
         _iter = QTreeWidgetItemIterator(treeWidget)
         index = 0
-        while(_iter.value()):
+        while (_iter.value()):
             item = _iter.value()
             self.assertTrue(item is items[index])
             index += 1
index 414b3d829078c2c396185de40fb8e06e49977b07..be6700d83c9e00a74e1f77e90524b79e1e928a52 100644 (file)
@@ -17,7 +17,7 @@ from PySide6.QtWidgets import QApplication, QGraphicsView, QGraphicsScene, QGrap
 class Ball(QGraphicsEllipseItem):
     def __init__(self, d, parent=None):
         super().__init__(0, 0, d, d, parent)
-        self.vel = QPointF(0, 0)   #commenting this out prevents the crash
+        self.vel = QPointF(0, 0)   # commenting this out prevents the crash
 
 
 class Foo(QGraphicsView):
index f99bf6b08a87d358614637b0e05db6647798ebbe..1dbfd4b3a10529cd64ac9f0a1a94bc64f6da2d68 100644 (file)
@@ -10,8 +10,8 @@ sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
 from init_paths import init_test_paths
 init_test_paths(False)
 
-from PySide6.QtWidgets import QTableView, QVBoxLayout, QApplication
-from PySide6.QtCore import QAbstractItemModel
+from PySide6.QtWidgets import QTableView, QApplication
+from PySide6.QtCore import QAbstractItemModel, QModelIndex
 
 from helper.usesqapplication import UsesQApplication
 
index f63d8a565e56b89e5e108213f4b738d8807c48a5..d995c2a372b59c7ea3324651f539158b5a9158b1 100644 (file)
@@ -26,7 +26,7 @@ class MyWidget(QWidget):
 
     def __init__(self, app):
         # Creates a new widget
-        assert(app)
+        assert (app)
 
         super().__init__()
         self.app = app
index 4ec8cb981d7bb5f423c5b8995211d64ebf6ba7b2..aba90b7b85e8e1ed91298e4b0b2d059450ef93b7 100644 (file)
@@ -64,7 +64,7 @@ class QLabelTest(UsesQApplication):
         # address that our QPixmap p1 because it was deleted
         # using `del ret_p`
         self.assertTrue(all(Shiboken.getCppPointer(o) != ret_p_addr
-                   for o in Shiboken.getAllValidWrappers()))
+                        for o in Shiboken.getAllValidWrappers()))
 
     # Test for PYSIDE-1673, QObject.property() returning a QFlags<> property.
     def testQObjectProperty(self):
index 8f93c6f93b49b17f7afc956329eed8182e2e5f7f..a6976a637977b4e34238eaca61d1ec966f0f6e2c 100644 (file)
@@ -37,11 +37,11 @@ class QMenuAddAction(UsesQApplication):
     def testAddActionKeySequenceCallable(self):
         # bug #228
         action = self.menu.addAction(self.app.tr('aaa'), lambda: 1,
-                                    QKeySequence(self.app.tr('Ctrl+O')))
+                                     QKeySequence(self.app.tr('Ctrl+O')))
 
     def testAddActionKeySequenceSlot(self):
         action = self.menu.addAction('Quit', self.app, SLOT('quit()'),
-                                    QKeySequence('Ctrl+O'))
+                                     QKeySequence('Ctrl+O'))
 
 
 class QMenuAddActionWithIcon(UsesQApplication):
@@ -65,11 +65,11 @@ class QMenuAddActionWithIcon(UsesQApplication):
     def testAddActionKeySequenceCallable(self):
         # bug #228
         action = self.menu.addAction(self.icon, self.app.tr('aaa'), lambda: 1,
-                                    QKeySequence(self.app.tr('Ctrl+O')))
+                                     QKeySequence(self.app.tr('Ctrl+O')))
 
     def testAddActionKeySequenceSlot(self):
         action = self.menu.addAction(self.icon, 'Quit', self.app, SLOT('quit()'),
-                                    QKeySequence('Ctrl+O'))
+                                     QKeySequence('Ctrl+O'))
 
 
 if __name__ == '__main__':
index 9b8872f84e9d086503e47482c9290d5f3094a382..5a9b29dc63eb38855efa074641621042e3028c08 100644 (file)
@@ -15,7 +15,7 @@ from helper.usesqapplication import UsesQApplication
 
 from PySide6.QtGui import QWindow
 from PySide6.QtWidgets import (QApplication, QFontComboBox, QLabel, QProxyStyle,
-    QStyleFactory, QWidget)
+                               QStyleFactory, QWidget)
 
 
 class ProxyStyle(QProxyStyle):
index 6e046e9d72b88a1a90889523724eb79681c6e19d..fd535bfad693b81b8e8b927d4cddb7e73a7504a2 100644 (file)
@@ -13,7 +13,7 @@ init_test_paths(False)
 
 from PySide6.QtGui import QStandardItemModel
 from PySide6.QtWidgets import (QWidget, QTreeView, QVBoxLayout,
-    QStyledItemDelegate, QHeaderView)
+                               QStyledItemDelegate, QHeaderView)
 from PySide6.QtCore import Qt
 from helper.usesqapplication import UsesQApplication
 
index edb4855c197d07bf3c9d312d736feff40c80c09e..fe0266309d4c012aa69627bb1ceb24044bc8188a 100644 (file)
@@ -13,7 +13,7 @@ init_test_paths(False)
 
 from PySide6.QtCore import Qt, QObject
 from PySide6.QtWidgets import (QComboBox, QGraphicsScene,
-    QGraphicsRectItem)
+                               QGraphicsRectItem)
 
 from helper.usesqapplication import UsesQApplication
 
@@ -114,8 +114,8 @@ class QVariantConversionTest(UsesQApplication):
 
     def testContainerTypes(self):
         #list
-        self.obj.setProperty("test", [1,2,3])
-        self.assertEqual(self.obj.property("test"), [1,2,3])
+        self.obj.setProperty("test", [1, 2, 3])
+        self.assertEqual(self.obj.property("test"), [1, 2, 3])
         self.assertTrue(isinstance(self.obj.property("test"), list))
         #dict
         self.obj.setProperty("test", {1: "one"})
index 9f1a9d43c3f0c386ab3839e316cca903f23446c2..ec6e398218c2c90e532c4feef565de9417cb4def 100644 (file)
@@ -36,7 +36,7 @@ class ConstructorPropertiesTest(unittest.TestCase):
     def testCallConstructor(self):
         label = QLabel(
             frameStyle=QFrame.Panel | QFrame.Sunken,    # QFrame attr, no property
-            lineWidth = 2,                              # QFrame property
+            lineWidth=2,                                # QFrame property
             text="first line\nsecond line",             # QLabel property
             alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
         )
@@ -52,13 +52,13 @@ class ConstructorPropertiesTest(unittest.TestCase):
 
         label = QLabel(
             frame_style=QFrame.Panel | QFrame.Sunken,   # QFrame attr, no property
-            line_width = 2,                             # QFrame property
+            line_width=2,                               # QFrame property
             text="first line\nsecond line",             # QLabel property
             alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
         )
         self.assertEqual(label.line_width(), 2)
         self.assertRaises(AttributeError, lambda: QLabel(
-            lineWidth = 2,                              # QFrame property
+            lineWidth=2,                                # QFrame property
         ))
 
     # PYSIDE-1705: The same with true_property
@@ -68,13 +68,13 @@ class ConstructorPropertiesTest(unittest.TestCase):
 
         label = QLabel(
             frameStyle=QFrame.Panel | QFrame.Sunken,    # QFrame attr, no property
-            lineWidth = 2,                              # QFrame property
+            lineWidth=2,                                # QFrame property
             text="first line\nsecond line",             # QLabel property
             alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
         )
         self.assertEqual(label.lineWidth, 2)
         self.assertRaises(AttributeError, lambda: QLabel(
-            line_width = 2,                             # QFrame property
+            line_width=2,                               # QFrame property
         ))
 
     # PYSIDE-1705: The same with snake_case and true_property
@@ -84,13 +84,13 @@ class ConstructorPropertiesTest(unittest.TestCase):
 
         label = QLabel(
             frame_style=QFrame.Panel | QFrame.Sunken,   # QFrame attr, no property
-            line_width = 2,                             # QFrame property
+            line_width=2,                               # QFrame property
             text="first line\nsecond line",             # QLabel property
             alignment=Qt.AlignBottom | Qt.AlignRight    # QLabel property
         )
         self.assertEqual(label.line_width, 2)
         self.assertRaises(AttributeError, lambda: QLabel(
-            lineWidth = 2,                              # QFrame property
+            lineWidth=2,                                # QFrame property
         ))
 
 
index 68c3638cdc1f95239f736d27c0820f912b7a53b3..c83e1f26c7430be0230bf6b2d269531370c43f5f 100644 (file)
@@ -44,13 +44,13 @@ class ContainerTestTest(unittest.TestCase):
         self.assertEqual(sort_values(m2), EXPECTED_DICT)
 
     def testList(self):
-        l1 = ContainerTest.createList();
+        l1 = ContainerTest.createList()
         self.assertEqual(l1, EXPECTED_LIST)
         l2 = ContainerTest.passThroughList(l1)
         self.assertEqual(l2, EXPECTED_LIST)
 
     def testSet(self):
-        s1 = ContainerTest.createSet();  # Order is not predictable
+        s1 = ContainerTest.createSet()  # Order is not predictable
         s2 = ContainerTest.passThroughSet(s1)
         self.assertEqual(sorted(list(s1)), sorted(list(s2)))
 
index 189ef60c10c6af8512347b556cb1025b0696815d..83283453043661810af1143c2843e017022799a4 100644 (file)
@@ -15,6 +15,7 @@ from testbinding import Enum1, TestObjectWithoutNamespace
 
 import dis
 
+
 class ListConnectionTest(unittest.TestCase):
 
     def testEnumVisibility(self):
@@ -45,6 +46,7 @@ class ListConnectionTest(unittest.TestCase):
         self.assertFalse(Qt.AlignBottom < Qt.AlignHCenter)
         self.assertTrue(Qt.AlignBottom > Qt.AlignHCenter)
 
+
 # PYSIDE-1735: We are testing that opcodes do what they are supposed to do.
 #              This is needed in the PyEnum forgiveness mode where we need
 #              to introspect the code if an Enum was called with no args.
@@ -69,6 +71,7 @@ class InvestigateOpcodesTest(unittest.TestCase):
         return sorted(res, key=lambda x: (x[1], x[0]))
 
     _sin = sys.implementation.name
+
     @unittest.skipIf(hasattr(sys.flags, "nogil"), f"{_sin} has different opcodes")
     def testByteCode(self):
         import dis
@@ -134,7 +137,7 @@ class InvestigateOpcodesTest(unittest.TestCase):
                 for _ in range(times):
                     f()
 
-            code_quicken(self.probe_function2, QUICKENING_WARMUP_DELAY-1)
+            code_quicken(self.probe_function2, QUICKENING_WARMUP_DELAY - 1)
             self.assertEqual(self.read_code(self.probe_function2, adaptive=True), result_2)
             self.assertEqual(self.get_sizes(self.probe_function2, adaptive=True), sizes_2)
 
@@ -174,7 +177,6 @@ class InvestigateOpcodesTest(unittest.TestCase):
                         ('STORE_FAST', 125, 1),
                         ('RETURN_CONST', 121, 0)]
 
-
         self.assertEqual(self.read_code(self.probe_function1), result_1)
         self.assertEqual(self.read_code(self.probe_function2), result_2)
 
index 52c65e54114d2d9000794255033dd3ff876b6f01..b58232a1b820525c58cc8d93e9ccc3c92ee8359f 100644 (file)
@@ -99,8 +99,8 @@ class HomonymousMultipleInheritanceTest(unittest.TestCase):
 
     def testHomonymousMultipleInheritance(self):
         c = C()
-        self.assertEqual(c.method(), "M::method") # okay
-        self.assertEqual(c.signal(), "M::signal") # problem on PySide6 6.2.2
+        self.assertEqual(c.method(), "M::method")  # okay
+        self.assertEqual(c.signal(), "M::signal")  # problem on PySide6 6.2.2
         self.assertEqual(type(c.signal), SignalInstance)
 
 
index 033a065abd94ffd08f2e1963a60bfbcda3462707..fe8e14f9f7f7c87e6a840305a85e4a04fce051bc 100644 (file)
@@ -19,6 +19,7 @@ def xprint(*args, **kw):
     if "-v" in sys.argv:
         print(*args, **kw)
 
+
 # This is the original testcase of PYSIDE-1564
 class Age(object):
     def __init__(self, age=0, **kwds):
@@ -26,6 +27,7 @@ class Age(object):
 
         self.age = age
 
+
 class Person(QtCore.QObject, Age):
     def __init__(self, name, **kwds):
         super().__init__(**kwds)
@@ -43,6 +45,7 @@ class OriginalMultipleInheritanceTest(unittest.TestCase):
 
 # More tests follow:
 
+
 # mro ('C', 'A', 'QObject', 'Object', 'B', 'object')
 class A(QtCore.QObject):
     def __init__(self, anna=77, **kw):
@@ -50,6 +53,7 @@ class A(QtCore.QObject):
         super().__init__(**kw)
         xprint('A: after init')
 
+
 class B:
     def __init__(self, otto=6, age=7, **kw):
         xprint(f'B: before init kw = {kw}')
@@ -59,12 +63,14 @@ class B:
         self.age = age
         xprint('B: after init')
 
+
 class C(A, B):
     def __init__(self, **kw):
         xprint(f'C: before init kw = {kw}')
         super().__init__(**kw)
         xprint('C: after init')
 
+
 # mro ('F', 'D', 'QCursor', 'E', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object')
 class D(QtGui.QCursor):
     def __init__(self, anna=77, **kw):
@@ -72,6 +78,7 @@ class D(QtGui.QCursor):
         super().__init__(**kw)
         xprint('D: after init')
 
+
 class E:
     def __init__(self, age=7, **kw):
         xprint(f'E: before init kw = {kw}')
@@ -79,17 +86,20 @@ class E:
         self.age = age
         xprint('E: after init')
 
+
 class F(D, E, QtWidgets.QLabel):
     def __init__(self, **kw):
         xprint(f'F: before init kw = {kw}')
         super().__init__(**kw)
         xprint('F: after init')
 
+
 # mro ('I', 'G', 'QTextDocument', 'H', 'QLabel', 'QFrame', 'QWidget', 'QObject', 'QPaintDevice', 'Object', 'object')
 # Similar, but this time we want to reach `H` without support from `super`.
 class G(QtGui.QTextDocument):
     pass
 
+
 class H:
     def __init__(self, age=7, **kw):
         xprint(f'H: before init kw = {kw}')
@@ -97,6 +107,7 @@ class H:
         self.age = age
         xprint('H: after init')
 
+
 class I(G, H, QtWidgets.QLabel):
     pass
 
@@ -108,6 +119,7 @@ class Ui_X_MainWindow(object):  # Emulating uic
         MainWindow.resize(400, 300)
         self.lbl = QLabel(self)
 
+
 class MainWindow(QMainWindow, Ui_X_MainWindow):
     def __init__(self, parent=None):
         super().__init__(parent)
@@ -122,7 +134,7 @@ class AdditionalMultipleInheritanceTest(UsesQApplication):
         self.assertEqual(res.age, 7)
         xprint()
         with self.assertRaises(AssertionError):
-            res=C(killme=42)
+            res = C(killme=42)
         xprint()
 
     def testDEF(self):
index 5ccbfb92e40b199e20ea50257a558c6537564f92..625f9cdc5c7c9995dbc9d761f2d7a66d6e9088db 100644 (file)
@@ -49,6 +49,7 @@ def runtest(program):
         finally:
             os.unlink(fp.name)
 
+
 def testprog2(option):
     return runtest(dedent(f"""
         sys.pyside6_option_python_enum = {option}
@@ -57,6 +58,7 @@ def testprog2(option):
         assert(issubclass(QtCore.Qt.DateFormat, IntEnum))
         """))
 
+
 def testprog4(option):
     return runtest(dedent(f"""
         sys.pyside6_option_python_enum = {option}
@@ -64,6 +66,7 @@ def testprog4(option):
         QtCore.QtDebugMsg
         """))
 
+
 def testprog8_16(option):
     # this test needs flag 16, or the effect would be hidden by forgiving mode
     return runtest(dedent(f"""
@@ -72,6 +75,7 @@ def testprog8_16(option):
         QtCore.Qt.AlignTop
         """))
 
+
 def testprog32(option):
     return runtest(dedent(f"""
         sys.pyside6_option_python_enum = {option}
@@ -79,6 +83,7 @@ def testprog32(option):
         QtCore.Qt.Alignment
         """))
 
+
 def testprog64(option):
     return runtest(dedent(f"""
         sys.pyside6_option_python_enum = {option}
@@ -86,6 +91,7 @@ def testprog64(option):
         QtCore.Qt.AlignmentFlag()
         """))
 
+
 def testprog128(option):
     return runtest(dedent(f"""
         sys.pyside6_option_python_enum = {option}
@@ -110,8 +116,8 @@ class TestPyEnumRelaxOption(unittest.TestCase):
         self.assertTrue(testprog4(12))
 
     def test_localDefault(self):
-        self.assertTrue(testprog8_16(8+16))
-        self.assertFalse(testprog8_16(0+16))
+        self.assertTrue(testprog8_16(8 + 16))
+        self.assertFalse(testprog8_16(0 + 16))
 
     def test_fakeRenames(self):
         self.assertTrue(testprog32(1))
index d09d63c0ea02b95927e31677da317dcc11c12b77..5faaa38d48999964e03283645e35e7e161634f22 100644 (file)
@@ -40,6 +40,7 @@ class TestSignalInstance(unittest.TestCase):
     def test_custom_inherited_signal_instances_are_equal(self):
         o = D()
         self.assertTrue(o.custom_signal == o.custom_signal)
+
     # additional tests of old errors from 2010 or so
     def test_uninitialized_SignalInstance(self):
         # This will no longer crash
@@ -51,6 +52,7 @@ class TestSignalInstance(unittest.TestCase):
         with self.assertRaises(RuntimeError):
             SignalInstance().emit()
 
+
 class MyWidget(QSlider):
     valueChanged = Signal(tuple)
 
index 5056d50bb9e3609cecfa9413cba5a43fbd51f305..4a482c35a12498e6b5895d976376ca87db842b41 100644 (file)
@@ -16,6 +16,7 @@ PYSIDE-2029: Tests that snake_case is isolated from imported modules
 
 from PySide6.QtWidgets import QWidget
 
+
 def test_no_snake_case():
     print(__name__)
     widget = QWidget()
index aaa3d3f2a9129fbfd3ddbcd1514efbef0f23cdcb..14e03577306f4bfd1f34ac5748d99049f118d56d 100644 (file)
@@ -13,14 +13,17 @@ init_test_paths(False)
 """
 PYSIDE-2029: Tests that snake_case is isolated from imported modules
 """
+is_pypy = hasattr(sys, "pypy_version_info")
 
 from PySide6.QtCore import QSize
 from PySide6.QtWidgets import QWidget, QSpinBox
-from __feature__ import snake_case
+if not is_pypy:
+    from __feature__ import snake_case
 from helper.usesqapplication import UsesQApplication
 
 import snake_case_sub
 
+@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
 class SnakeCaseNoPropagateTest(UsesQApplication):
 
     def testSnakeCase(self):
@@ -30,5 +33,6 @@ class SnakeCaseNoPropagateTest(UsesQApplication):
 
         snake_case_sub.test_no_snake_case()
 
+
 if __name__ == '__main__':
     unittest.main()
index 0b9a909b761c4a1d7efd1c3690ae02a609d4f905..62f6505dcf243d2eda4a84ba371d3dae6d07655a 100644 (file)
@@ -13,13 +13,16 @@ init_test_paths(False)
 """
 PYSIDE-2042: Tests true_property with inheritance
 """
+is_pypy = hasattr(sys, "pypy_version_info")
 
 from PySide6.QtCore import QSize
 from PySide6.QtWidgets import QWidget, QSpinBox
-from __feature__ import true_property
+if not is_pypy:
+    from __feature__ import true_property
 from helper.usesqapplication import UsesQApplication
 
 
+@unittest.skipIf(is_pypy, "__feature__ cannot yet be used with PyPy")
 class TruePropertyInheritanceTest(UsesQApplication):
 
     def testTrueProperty(self):
@@ -43,6 +46,7 @@ class TruePropertyInheritanceTest(UsesQApplication):
         self.assertTrue(hasattr(widget, "setVisible"))
         self.assertEqual(widget.isVisible, QWidget.visible.fget)
         self.assertEqual(widget.setVisible, QWidget.visible.fset)
+
         # This works with inheritance as well:
         class SubClass(QWidget):
             pass
index a6e3b048bd05bce72843d875ac3427e36c4010cc..b7b6b58aab5805187d8afb98befe82bdf42f90ba 100644 (file)
@@ -212,7 +212,7 @@ def handle_suburl(idx, n, url, level):
                 print(os.getpid(), test_name)
                 response = read_url(sub_url)
                 txt = response.text if response else ''
-                if "BEGIN_FILE" in txt and not "'BEGIN_FILE'" in txt:
+                if "BEGIN_FILE" in txt and "'BEGIN_FILE'" not in txt:
                     # find the text, but not a traceback with that text
                     print(os.getpid(), test_name, "FOUND!")
                     write_data(test_name, response.text)
index 0359d28c41d3b8a7c8318ae9374c22e02c30969f..a1c5e05e59dbfc182a6d5571f547c8c24aee0c82 100644 (file)
@@ -26,8 +26,6 @@ def isolate_warnings():
         if mod and hasattr(mod, warn_name):
             save_warnings[name] = mod.__dict__[warn_name]
             delattr(mod, warn_name)
-        else:
-            save_warnings[name] = None
     yield
     for name, warn in save_warnings.items():
         mod = sys.modules[name]
index 0d7e574efac0cf6112d311460d535d963f1b32e5..b5252870c26bfde5d0cbae698f51cab5feaa4734 100644 (file)
@@ -35,7 +35,7 @@ class InvalidCallback(unittest.TestCase):
     def testIntegerCb(self):
         # Test passing an int as callback to QObject.connect
         self.assertRaises(TypeError, QObject.connect, self.obj,
-                            SIGNAL('destroyed()'), 42)
+                          SIGNAL('destroyed()'), 42)
 
 
 if __name__ == '__main__':
index a7cda69f72bd52b73fbc1f16a1a48489288681ae..485584ec2fe4c18583a948d362878ef13db9dff0 100644 (file)
@@ -73,6 +73,12 @@ class ObjectSenderWithQAppTest(UsesQApplication):
         self.app.exec()
         self.assertTrue(isinstance(recv.the_sender, QObject))
 
+    def testSenderCppSignalSingleShotTimerWithContext(self):
+        recv = Receiver()
+        QTimer.singleShot(10, recv, recv.callback)
+        self.app.exec()
+        self.assertTrue(isinstance(recv.the_sender, QObject))
+
     def testSenderCppSignalWithPythonExtendedClass(self):
         sender = ExtQTimer()
         recv = Receiver()
@@ -105,4 +111,3 @@ class ObjectSenderWithQAppCheckOnReceiverTest(UsesQApplication):
 
 if __name__ == '__main__':
     unittest.main()
-
index f6c20ee2e05ff8e456f7a6b280d710702cd85f93..b773b7c58f044b102559f817db7c4a21d2f79ee9 100644 (file)
@@ -19,8 +19,7 @@ from inspect import isclass
 ignore = ["staticMetaObject",
           "pyqtConfigure",
           "registerUserData",
-          "thread",
-         ]
+          "thread"]
 
 
 def recurse_into(el, obj):
@@ -47,7 +46,7 @@ def recurse_into(el, obj):
     return symbols
 
 
-if __name__=='__main__':
+if __name__ == '__main__':
     modules = [ 'QtCore',
                 'QtGui',
                 'QtHelp',
index 5801c6fdcbc8b4524e371ed942d453e8ad351f05..81c2c9a4ae02c00e5a1253f230c68faef9e0d484 100644 (file)
@@ -1,45 +1,46 @@
 # Copyright (C) 2023 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
-import unittest
-import tempfile
+import importlib
+import os
+import re
 import shutil
 import sys
-import os
-import importlib
+import tempfile
+import unittest
+import subprocess
 from pathlib import Path
-from configparser import ConfigParser
-from unittest.mock import patch
 from unittest import mock
+from unittest.mock import patch
 
-
-class ConfigFile:
-    def __init__(self, config_file: Path) -> None:
-        self.config_file = config_file
-        self.parser = ConfigParser(comment_prefixes="/", allow_no_value=True)
-        self.parser.read(self.config_file)
-
-    def get_value(self, section: str, key: str):
-        return str(self.parser.get(section, key))
+sys.path.append(os.fspath(Path(__file__).resolve().parents[2]))
+from init_paths import init_test_paths
+init_test_paths(False)
 
 
-class TestPySide6AndroidDeploy(unittest.TestCase):
+class DeployTestBase(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls.pyside_root = Path(__file__).parents[5].resolve()
         cls.example_root = cls.pyside_root / "examples"
-        example_widget_application = cls.example_root / "gui" / "analogclock"
         cls.temp_dir = tempfile.mkdtemp()
-        cls.temp_example = Path(
-            shutil.copytree(example_widget_application, Path(cls.temp_dir) / "analogclock")
-        ).resolve()
         cls.current_dir = Path.cwd()
-        cls.pyside_wheel = Path("tmp/PySide6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
-        cls.shiboken_wheel = Path("tmp/shiboken6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
-        cls.ndk_path = Path("tmp/android_sdk/ndk/25.2.9519653")
-        cls.sdk_path = Path("tmp/android_sdk")
-
-        sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
+        cls.pyside_wheel = Path("/tmp/PySide6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
+        cls.shiboken_wheel = Path("/tmp/shiboken6-6.5.0a1-6.5.0-cp37-abi3-android_x86_64.whl")
+        cls.ndk_path = Path("/tmp/android_sdk/ndk/25.2.9519653")
+        cls.sdk_path = Path("/tmp/android_sdk")
+        pyside_tools = cls.pyside_root / "sources" / "pyside-tools"
+
+        # install extra python dependencies
+        android_requirements_file = pyside_tools / "requirements-android.txt"
+        with open(android_requirements_file, 'r', encoding='UTF-8') as file:
+            while line := file.readline():
+                dependent_package = line.rstrip()
+                if not bool(importlib.util.find_spec(dependent_package)):
+                    command = [sys.executable, "-m", "pip", "install", dependent_package]
+                    subprocess.run(command)
+
+        sys.path.append(str(pyside_tools))
         cls.deploy_lib = importlib.import_module("deploy_lib")
         cls.android_deploy = importlib.import_module("android_deploy")
         sys.modules["android_deploy"] = cls.android_deploy
@@ -50,127 +51,213 @@ class TestPySide6AndroidDeploy(unittest.TestCase):
         # print no outputs to stdout
         sys.stdout = mock.MagicMock()
 
+    def tearDown(self) -> None:
+        super().tearDown()
+        os.chdir(self.current_dir)
+
+    @classmethod
+    def tearDownClass(cls) -> None:
+        shutil.rmtree(Path(cls.temp_dir))
+
+
+@patch("deploy_lib.android.android_config.extract_and_copy_jar")
+class TestPySide6AndroidDeployWidgets(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_widget_application = cls.example_root / "gui" / "analogclock"
+        cls.temp_example = Path(
+            shutil.copytree(example_widget_application, Path(cls.temp_dir) / "analogclock")
+        ).resolve()
+
     def setUp(self):
         os.chdir(self.temp_example)
         self.config_file = self.temp_example / "pysidedeploy.spec"
+        self.buildozer_config = self.temp_example / "buildozer.spec"
 
-    @patch("android_deploy.extract_and_copy_jar")
-    @patch("android_deploy.Wheel")
-    def test_dry_run(self, mock_jar, mock_wheel):
-        mock_wheel.version = "6.5.0a1"
-
-        # test if dry_run works without errors
+    def test_dry_run(self, mock_extract_jar):
         self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
-                                 pyside_wheel=self.pyside_wheel, dry_run=True, force=True)
-
-        self.assertEqual(mock_wheel.call_count, 1)
-        self.assertEqual(mock_jar.call_count, 1)
-        self.assertFalse(self.config_file.exists())
-
-    @patch("android_deploy.extract_and_copy_jar")
-    @patch("android_deploy.Wheel")
-    def test_config(self, mock_jar, mock_wheel):
-        '''
-            Tests config options from the dynamically created buildozer.spec and pysidedeploy.spec
-        '''
-        mock_wheel.version = "6.5.0a1"
+                                 pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
+                                 dry_run=True, force=True)
+        self.assertEqual(mock_extract_jar.call_count, 0)
+
+    @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
+    @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
+    @patch("deploy_lib.android.buildozer.BuildozerConfig."
+           "_BuildozerConfig__find_dependent_qt_modules")
+    @patch("deploy_lib.android.buildozer.find_qtlibs_in_wheel")
+    def test_config(self, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist, mock_find_jars,
+                    mock_extract_jar):
+        jar_dir = "tmp/jar/PySide6/jar"
+        mock_extract_jar.return_value = Path(jar_dir)
+        mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+        mock_extraqtmodules.return_value = []
+        mock_recipes_exist.return_value = True
+        jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar",
+                              "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar"], []
+        mock_find_jars.return_value = jars, init_classes
 
         self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
-                                 pyside_wheel=self.pyside_wheel, init=True, force=True)
+                                 pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
+                                 init=True, force=True, keep_deployment_files=True)
 
-        self.assertEqual(mock_wheel.call_count, 1)
-        self.assertEqual(mock_jar.call_count, 1)
+        self.assertEqual(mock_extract_jar.call_count, 1)
+        self.assertEqual(mock_qtlibs.call_count, 1)
+        self.assertEqual(mock_extraqtmodules.call_count, 1)
+        self.assertEqual(mock_recipes_exist.call_count, 1)
+        self.assertEqual(mock_find_jars.call_count, 1)
         self.assertTrue(self.config_file.exists())
+        self.assertTrue(self.buildozer_config.exists())
 
         # test config file contents
-        config_obj = ConfigFile(config_file=self.config_file)
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
         self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
         self.assertEqual(config_obj.get_value("python", "android_packages"),
                          "buildozer==1.5.0,cython==0.29.33")
-        self.assertEqual(config_obj.get_value("qt", "wheel_pyside"),
+        self.assertEqual(config_obj.get_value("android", "wheel_pyside"),
                          str(self.pyside_wheel.resolve()))
-        self.assertEqual(config_obj.get_value("qt", "wheel_shiboken"),
+        self.assertEqual(config_obj.get_value("android", "wheel_shiboken"),
                          str(self.shiboken_wheel.resolve()))
         self.assertEqual(config_obj.get_value("buildozer", "mode"), "debug")
         self.assertEqual(config_obj.get_value("buildozer", "recipe_dir"),
-                         str(self.temp_example / "deployment" / "recipes"))
+                         '')
         self.assertEqual(config_obj.get_value("buildozer", "jars_dir"),
-                         str(self.temp_example / "deployment" / "jar" / "PySide6" / "jar"))
-        self.assertEqual(config_obj.get_value("buildozer", "ndk_path"), "")
-        self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), "")
-        self.assertEqual(config_obj.get_value("buildozer", "modules"), "Core,Gui,Widgets")
+                         str(self.temp_example / jar_dir))
+        self.assertIn(str(self.ndk_path), config_obj.get_value("buildozer", "ndk_path"))
+        self.assertEqual(config_obj.get_value("buildozer", "sdk_path"), '')
+        expected_modules = {"Core", "Gui"}
+        obtained_modules = set(config_obj.get_value("buildozer", "modules").split(","))
+        self.assertEqual(obtained_modules, expected_modules)
+        expected_local_libs = "plugins_platforms_qtforandroid"
         self.assertEqual(config_obj.get_value("buildozer", "local_libs"),
-                         "plugins_platforms_qtforandroid")
+                         expected_local_libs)
         self.assertEqual(config_obj.get_value("buildozer", "arch"), "x86_64")
-        self.config_file.unlink()
-
-    @patch("android_deploy.extract_and_copy_jar")
-    @patch("android_deploy.Wheel")
-    def test_config_with_ndk_sdk(self, mock_jar, mock_wheel):
-        mock_wheel.version = "6.5.0a1"
 
-        self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
-                                 pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
-                                 sdk_path=self.sdk_path, init=True, force=True)
-
-        self.assertEqual(mock_wheel.call_count, 1)
-        self.assertEqual(mock_jar.call_count, 1)
-        self.assertTrue(self.config_file.exists())
+        # test buildozer config file contents
+        buildozer_config_obj = self.deploy_lib.BaseConfig(config_file=self.buildozer_config)
+        obtained_jars = set(buildozer_config_obj.get_value("app", "android.add_jars").split(','))
+        expected_jars = set(jars)
+        self.assertEqual(obtained_jars, expected_jars)
+        obtained_extra_args = buildozer_config_obj.get_value("app", "p4a.extra_args")
+        extra_args_patrn = re.compile("--qt-libs=(?P<modules>.*) --load-local-libs="
+                                      "(?P<local_libs>.*) --init-classes=(?P<init_classes>.*)")
+        match = extra_args_patrn.search(obtained_extra_args)
+        obtained_modules = match.group("modules").split(',')
+        obtained_local_libs = match.group("local_libs")
+        obtained_init_classes = match.group("init_classes")
+        self.assertEqual(set(obtained_modules), expected_modules)
+        self.assertEqual(obtained_local_libs, expected_local_libs)
+        self.assertEqual(obtained_init_classes, '')
+        expected_include_exts = "py,png,jpg,kv,atlas,qml,js"
+        obtained_include_exts = buildozer_config_obj.get_value("app", "source.include_exts")
+        self.assertEqual(expected_include_exts, obtained_include_exts)
 
-        # test config file contents
-        config_obj = ConfigFile(config_file=self.config_file)
-        self.assertEqual(config_obj.get_value("buildozer", "ndk_path"),
-                         str(self.ndk_path.resolve()))
-        self.assertEqual(config_obj.get_value("buildozer", "sdk_path"),
-                         str(self.sdk_path.resolve()))
         self.config_file.unlink()
+        self.buildozer_config.unlink()
 
-    def test_error_pwd_not_projectdir(self):
-        os.chdir(self.current_dir)
-        with self.assertRaises(RuntimeError):
+    def test_errors(self, mock_extract_jar):
+        # test if error raises for non existing NDK
+        with self.assertRaises(FileNotFoundError) as context:
             self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
-                                     pyside_wheel=self.pyside_wheel, init=True, force=True)
+                                     pyside_wheel=self.pyside_wheel, force=True)
+        self.assertTrue("Unable to find Android NDK" in str(context.exception))
 
-    def test_error_no_wheels(self):
+        # test when cwd() is not project_dir
         os.chdir(self.current_dir)
-        with self.assertRaises(RuntimeError):
-            self.android_deploy.main(name="android_app", shiboken_wheel=None,
+        with self.assertRaises(RuntimeError) as context:
+            self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
                                      pyside_wheel=self.pyside_wheel, init=True, force=True)
+        self.assertTrue("For Android deployment to work" in str(context.exception))
 
-    @patch("android_deploy.extract_and_copy_jar")
-    @patch("android_deploy.Wheel")
-    def test_config_with_Qml(self, mock_jar, mock_wheel):
-        example_qml_application = self.example_root / "quick" / "models" / "stringlistmodel"
-        temp_qml_example = Path(
-            shutil.copytree(example_qml_application, Path(self.temp_dir) / "stringlistmodel")
+
+@patch("deploy_lib.config.run_qmlimportscanner")
+@patch("deploy_lib.android.android_config.extract_and_copy_jar")
+class TestPySide6AndroidDeployQml(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        # setting up example
+        example_qml_application = cls.example_root / "quick" / "models" / "stringlistmodel"
+        cls.temp_qml_example = Path(
+            shutil.copytree(example_qml_application, Path(cls.temp_dir) / "stringlistmodel")
         ).resolve()
-        config_file = temp_qml_example / "pysidedeploy.spec"
-        (temp_qml_example / "stringlistmodel.py").rename(temp_qml_example / "main.py")
-        (temp_qml_example / "stringlistmodel.pyproject").unlink()
-        os.chdir(temp_qml_example)
 
-        mock_wheel.version = "6.5.0a1"
+    def setUp(self):
+        os.chdir(self.temp_qml_example)
+        self.config_file = self.temp_qml_example / "pysidedeploy.spec"
+        self.buildozer_config_file = self.temp_qml_example / "buildozer.spec"
+        (self.temp_qml_example / "stringlistmodel.py").rename(self.temp_qml_example / "main.py")
+        (self.temp_qml_example / "stringlistmodel.pyproject").unlink()
+
+    @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_local_libs")
+    @patch("deploy_lib.android.buildozer.BuildozerConfig._BuildozerConfig__find_jars")
+    @patch("deploy_lib.android.android_config.AndroidConfig.recipes_exist")
+    @patch("deploy_lib.android.buildozer.BuildozerConfig."
+           "_BuildozerConfig__find_dependent_qt_modules")
+    @patch("deploy_lib.android.buildozer.find_qtlibs_in_wheel")
+    def test_config_with_Qml(self, mock_qtlibs, mock_extraqtmodules, mock_recipes_exist,
+                             mock_find_jars, mock_local_libs, mock_extract_jar,
+                             mock_qmlimportscanner):
+        # setting up mocks
+        jar_dir = "tmp/jar/PySide6/jar"
+        mock_extract_jar.return_value = Path(jar_dir)
+        mock_qtlibs.return_value = self.pyside_wheel / "PySide6/Qt/lib"
+        mock_extraqtmodules.return_value = ['Qml', 'Network', 'QmlModels', 'OpenGL']
+        mock_recipes_exist.return_value = True
+        jars, init_classes = ["/tmp/jar/PySide6/jar/Qt6Android.jar",
+                              "/tmp/jar/PySide6/jar/Qt6AndroidBindings.jar",
+                              "/tmp/jar/PySide6/jar/Qt6AndroidNetworkInformationBackend.jar",
+                              "/tmp/jar/PySide6/jar/Qt6AndroidNetwork.jar"], []
+        mock_find_jars.return_value = jars, init_classes
+        dependent_plugins = ["platforms_qtforandroid",
+                             "platforminputcontexts_qtvirtualkeyboardplugin",
+                             "iconengines_qsvgicon"]
+        mock_local_libs.return_value = [], dependent_plugins
+        mock_qmlimportscanner.return_value = ["QtQuick"]
 
         self.android_deploy.main(name="android_app", shiboken_wheel=self.shiboken_wheel,
-                                 pyside_wheel=self.pyside_wheel, init=True, force=True)
-
-        self.assertEqual(mock_wheel.call_count, 1)
-        self.assertEqual(mock_jar.call_count, 1)
-        self.assertTrue(config_file.exists())
+                                 pyside_wheel=self.pyside_wheel, ndk_path=self.ndk_path,
+                                 init=True, force=True, keep_deployment_files=True)
+
+        self.assertEqual(mock_extract_jar.call_count, 1)
+        self.assertEqual(mock_qtlibs.call_count, 1)
+        self.assertEqual(mock_extraqtmodules.call_count, 1)
+        self.assertEqual(mock_recipes_exist.call_count, 1)
+        self.assertEqual(mock_find_jars.call_count, 1)
+        self.assertEqual(mock_qmlimportscanner.call_count, 1)
+        self.assertTrue(self.config_file.exists())
+        self.assertTrue(self.buildozer_config_file.exists())
 
         # test config file contents
-        config_obj = ConfigFile(config_file=config_file)
-        self.assertEqual(config_obj.get_value("buildozer", "modules"),
-                         "Core,Gui,Widgets,Network,OpenGL,Qml,Quick,QuickControls2")
-        config_file.unlink()
-
-    def tearDown(self) -> None:
-        super().tearDown()
-        os.chdir(self.current_dir)
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
+        expected_modules = {"Quick", "Core", "Gui", "Network", "Qml", "QmlModels", "OpenGL"}
+        obtained_modules = set(config_obj.get_value("buildozer", "modules").split(","))
+        self.assertEqual(obtained_modules, expected_modules)
+        expected_local_libs = "plugins_platforms_qtforandroid"
+        self.assertEqual(config_obj.get_value("buildozer", "local_libs"),
+                         expected_local_libs)
+        expected_qt_plugins = set(dependent_plugins)
+        obtained_qt_plugins = set(config_obj.get_value("android", "plugins").split(","))
+        self.assertEqual(expected_qt_plugins, obtained_qt_plugins)
+
+        # test buildozer config file contents
+        buildozer_config_obj = self.deploy_lib.BaseConfig(config_file=self.buildozer_config_file)
+        obtained_jars = set(buildozer_config_obj.get_value("app", "android.add_jars").split(','))
+        expected_jars = set(jars)
+        self.assertEqual(obtained_jars, expected_jars)
+        obtained_extra_args = buildozer_config_obj.get_value("app", "p4a.extra_args")
+        extra_args_patrn = re.compile("--qt-libs=(?P<modules>.*) --load-local-libs="
+                                      "(?P<local_libs>.*) --init-classes=(?P<init_classes>.*)")
+        match = extra_args_patrn.search(obtained_extra_args)
+        obtained_modules = match.group("modules").split(',')
+        obtained_local_libs = match.group("local_libs")
+        obtained_init_classes = match.group("init_classes")
+        self.assertEqual(set(obtained_modules), expected_modules)
+        self.assertEqual(obtained_local_libs, expected_local_libs)
+        self.assertEqual(obtained_init_classes, '')
 
-    @classmethod
-    def tearDownClass(cls) -> None:
-        shutil.rmtree(Path(cls.temp_dir))
+        self.config_file.unlink()
+        self.buildozer_config_file.unlink()
 
 
 if __name__ == "__main__":
index 290cc9807a9ca0170c3b570c5c9fcd76dc478d66..40afc7f5cf574cd46809272cd710a76e95d0c3f6 100644 (file)
@@ -56,7 +56,7 @@ class TestPySide6Deploy(unittest.TestCase):
                          keep_deployment_files=True, force=True)
 
         print("Now testing Widget with config file")
-        self.deploy.main(self.main_file, config_file=self.config_file,  loglevel=logging.INFO,
+        self.deploy.main(self.main_file, config_file=self.config_file, loglevel=logging.INFO,
                          force=True)
 
     def setUpQml(self):
index 3a54e2b8815a8a1277b35b897fede630b48fef41..9c743674613a6d84aa96077bec0760aa2e65e0f1 100644 (file)
@@ -8,49 +8,53 @@ import sys
 import os
 import importlib
 from pathlib import Path
-from configparser import ConfigParser
 from unittest.mock import patch
 from unittest import mock
 
 
-class ConfigFile:
-    def __init__(self, config_file: Path) -> None:
-        self.config_file = config_file
-        self.parser = ConfigParser(comment_prefixes="/", allow_no_value=True)
-        self.parser.read(self.config_file)
+def is_pyenv_python():
+    pyenv_root = os.environ.get("PYENV_ROOT")
 
-    def get_value(self, section: str, key: str):
-        return str(self.parser.get(section, key))
+    if pyenv_root and (resolved_exe := str(Path(sys.executable).resolve())):
+        return resolved_exe.startswith(pyenv_root)
+    return False
 
 
-class TestPySide6Deploy(unittest.TestCase):
+class LongSortedOptionTest(unittest.TestCase):
+    @staticmethod
+    def _option_prepare(s):
+        """
+        Take a string and return a list obtained by text.split().
+        Options starting with "--" are also sorted."
+        """
+        items = s.split()
+        for idx in range(len(items)):
+            if items[idx].startswith("--"):
+                return items[:idx] + sorted(items[idx:])
+        return items
+
+    def assertEqual(self, text_a, text_b):
+        if (not isinstance(text_a, str) or not isinstance(text_b, str)
+                or (len(text_a) < 50 and len(text_b) < 50)):
+            return super().assertEqual(text_a, text_b)
+        sort_a = self._option_prepare(text_a)
+        sort_b = self._option_prepare(text_b)
+        return super().assertEqual(sort_a, sort_b)
+
+
+class DeployTestBase(LongSortedOptionTest):
     @classmethod
     def setUpClass(cls):
-        # PYSIDE-2230: A temporary patch that avoids the pyenv error.
-        #              The final solution is too much for this quick fix.
-        if os.environ.get("PYENV_ROOT"):
-            del os.environ["PYENV_ROOT"]
         cls.pyside_root = Path(__file__).parents[5].resolve()
-        example_root = cls.pyside_root / "examples"
-        example_widgets = example_root / "widgets" / "widgets" / "tetrix"
-        example_qml = example_root / "qml" / "editingmodel"
-        example_webenginequick = example_root / "webenginequick" / "nanobrowser"
+        cls.example_root = cls.pyside_root / "examples"
         cls.temp_dir = tempfile.mkdtemp()
-        cls.temp_example_widgets = Path(
-            shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
-        ).resolve()
-        cls.temp_example_qml = Path(
-            shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
-        ).resolve()
-        cls.temp_example_webenginequick = Path(
-            shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
-        ).resolve()
         cls.current_dir = Path.cwd()
-        cls.linux_onefile_icon = (
-            cls.pyside_root / "sources" / "pyside-tools" / "deploy_lib" / "pyside_icon.jpg"
-        )
-
-        sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
+        tools_path = cls.pyside_root / "sources" / "pyside-tools"
+        cls.win_icon = tools_path / "deploy_lib" / "pyside_icon.ico"
+        cls.linux_icon = tools_path / "deploy_lib" / "pyside_icon.jpg"
+        cls.macos_icon = tools_path / "deploy_lib" / "pyside_icon.icns"
+        if tools_path not in sys.path:
+            sys.path.append(str(cls.pyside_root / "sources" / "pyside-tools"))
         cls.deploy_lib = importlib.import_module("deploy_lib")
         cls.deploy = importlib.import_module("deploy")
         sys.modules["deploy"] = cls.deploy
@@ -61,31 +65,54 @@ class TestPySide6Deploy(unittest.TestCase):
         # print no outputs to stdout
         sys.stdout = mock.MagicMock()
 
-    def setUpWidgets(self):
+    @classmethod
+    def tearDownClass(cls) -> None:
+        shutil.rmtree(Path(cls.temp_dir))
+
+    def tearDown(self) -> None:
+        super().tearDown()
+        os.chdir(self.current_dir)
+
+
+class TestPySide6DeployWidgets(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_widgets = cls.example_root / "widgets" / "widgets" / "tetrix"
+        cls.temp_example_widgets = Path(
+            shutil.copytree(example_widgets, Path(cls.temp_dir) / "tetrix")
+        ).resolve()
+
+    def setUp(self):
         os.chdir(self.temp_example_widgets)
         self.main_file = self.temp_example_widgets / "tetrix.py"
         self.deployment_files = self.temp_example_widgets / "deployment"
         self.expected_run_cmd = (
             f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports --onefile"
             f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
-            f" --noinclude-qt-translations=True"
+            f" --noinclude-qt-translations"
         )
         if sys.platform.startswith("linux"):
-            self.expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+            self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+        elif sys.platform == "darwin":
+            self.expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+        elif sys.platform == "win32":
+            self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+
+        if is_pyenv_python():
+            self.expected_run_cmd += " --static-libpython=no"
         self.config_file = self.temp_example_widgets / "pysidedeploy.spec"
 
     def testWidgetDryRun(self):
         # Checking for dry run commands is equivalent to mocking the
         # subprocess.check_call() in commands.py as the the dry run command
         # is the command being run.
-        self.setUpWidgets()
         original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
         self.assertEqual(original_output, self.expected_run_cmd)
 
     def testWidgetConfigFile(self):
         # includes both dry run and config_file tests
 
-        self.setUpWidgets()
         # init
         init_result = self.deploy.main(self.main_file, init=True, force=True)
         self.assertEqual(init_result, None)
@@ -95,19 +122,37 @@ class TestPySide6Deploy(unittest.TestCase):
         self.assertEqual(original_output, self.expected_run_cmd)
 
         # # test config file contents
-        config_obj = ConfigFile(config_file=self.config_file)
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
         self.assertEqual(config_obj.get_value("app", "input_file"), "tetrix.py")
         self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
         self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
-        self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.5.4,ordered_set,zstandard")
+        self.assertEqual(config_obj.get_value("python", "packages"),
+                         "nuitka==1.8.0,ordered_set,zstandard")
         self.assertEqual(config_obj.get_value("qt", "qml_files"), "")
-        self.assertEqual(
-            config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True"
-        )
+        equ_base = "--quiet --noinclude-qt-translations"
+        equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
+        self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
         self.assertEqual(config_obj.get_value("qt", "excluded_qml_plugins"), "")
         self.config_file.unlink()
 
-    def setUpQml(self):
+    def testErrorReturns(self):
+        # main file and config file does not exists
+        fake_main_file = self.main_file.parent / "main.py"
+        with self.assertRaises(RuntimeError) as context:
+            self.deploy.main(main_file=fake_main_file, config_file=self.config_file)
+        self.assertTrue("Directory does not contain main.py file." in str(context.exception))
+
+
+class TestPySide6DeployQml(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_qml = cls.example_root / "qml" / "editingmodel"
+        cls.temp_example_qml = Path(
+            shutil.copytree(example_qml, Path(cls.temp_dir) / "editingmodel")
+        ).resolve()
+
+    def setUp(self):
         os.chdir(self.temp_example_qml)
         self.main_file = self.temp_example_qml / "main.py"
         self.deployment_files = self.temp_example_qml / "deployment"
@@ -116,32 +161,37 @@ class TestPySide6Deploy(unittest.TestCase):
         self.expected_run_cmd = (
             f"{sys.executable} -m nuitka {str(self.main_file)} --follow-imports --onefile"
             f" --enable-plugin=pyside6 --output-dir={str(self.deployment_files)} --quiet"
-            f" --noinclude-qt-translations=True --include-qt-plugins=all"
+            f" --noinclude-qt-translations --include-qt-plugins=all"
             f" --include-data-files={str(self.temp_example_qml / self.first_qml_file)}="
             f"./main.qml --include-data-files="
             f"{str(self.temp_example_qml /self.second_qml_file)}=./MovingRectangle.qml"
-            )
+        )
 
         if sys.platform != "win32":
             self.expected_run_cmd += (
                 " --noinclude-dlls=libQt6Charts*"
                 " --noinclude-dlls=libQt6Quick3D* --noinclude-dlls=libQt6Sensors*"
                 " --noinclude-dlls=libQt6Test* --noinclude-dlls=libQt6WebEngine*"
-                )
+            )
         else:
             self.expected_run_cmd += (
                 " --noinclude-dlls=Qt6Charts*"
                 " --noinclude-dlls=Qt6Quick3D* --noinclude-dlls=Qt6Sensors*"
                 " --noinclude-dlls=Qt6Test* --noinclude-dlls=Qt6WebEngine*"
-                )
+            )
 
         if sys.platform.startswith("linux"):
-            self.expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+            self.expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+        elif sys.platform == "darwin":
+            self.expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+        elif sys.platform == "win32":
+            self.expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
+
+        if is_pyenv_python():
+            self.expected_run_cmd += " --static-libpython=no"
         self.config_file = self.temp_example_qml / "pysidedeploy.spec"
 
     def testQmlConfigFile(self):
-        self.setUpQml()
-
         # create config file
         with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
             mock_qmlimportscanner.return_value = ["QtQuick"]
@@ -149,15 +199,16 @@ class TestPySide6Deploy(unittest.TestCase):
             self.assertEqual(init_result, None)
 
         # test config file contents
-        config_obj = ConfigFile(config_file=self.config_file)
+        config_obj = self.deploy_lib.BaseConfig(config_file=self.config_file)
         self.assertEqual(config_obj.get_value("app", "input_file"), "main.py")
         self.assertEqual(config_obj.get_value("app", "project_dir"), ".")
         self.assertEqual(config_obj.get_value("app", "exec_directory"), ".")
-        self.assertEqual(config_obj.get_value("python", "packages"), "nuitka==1.5.4,ordered_set,zstandard")
+        self.assertEqual(config_obj.get_value("python", "packages"),
+                         "nuitka==1.8.0,ordered_set,zstandard")
         self.assertEqual(config_obj.get_value("qt", "qml_files"), "main.qml,MovingRectangle.qml")
-        self.assertEqual(
-            config_obj.get_value("nuitka", "extra_args"), "--quiet --noinclude-qt-translations=True"
-        )
+        equ_base = "--quiet --noinclude-qt-translations"
+        equ_value = equ_base + " --static-libpython=no" if is_pyenv_python() else equ_base
+        self.assertEqual(config_obj.get_value("nuitka", "extra_args"), equ_value)
         self.assertEqual(
             config_obj.get_value("qt", "excluded_qml_plugins"),
             "QtCharts,QtQuick3D,QtSensors,QtTest,QtWebEngine",
@@ -165,7 +216,6 @@ class TestPySide6Deploy(unittest.TestCase):
         self.config_file.unlink()
 
     def testQmlDryRun(self):
-        self.setUpQml()
         with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
             mock_qmlimportscanner.return_value = ["QtQuick"]
             original_output = self.deploy.main(self.main_file, dry_run=True, force=True)
@@ -173,13 +223,22 @@ class TestPySide6Deploy(unittest.TestCase):
             self.assertEqual(mock_qmlimportscanner.call_count, 1)
 
     def testMainFileDryRun(self):
-        self.setUpQml()
         with patch("deploy_lib.config.run_qmlimportscanner") as mock_qmlimportscanner:
             mock_qmlimportscanner.return_value = ["QtQuick"]
             original_output = self.deploy.main(Path.cwd() / "main.py", dry_run=True, force=True)
             self.assertEqual(original_output, self.expected_run_cmd)
             self.assertEqual(mock_qmlimportscanner.call_count, 1)
 
+
+class TestPySide6DeployWebEngine(DeployTestBase):
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        example_webenginequick = cls.example_root / "webenginequick" / "nanobrowser"
+        cls.temp_example_webenginequick = Path(
+            shutil.copytree(example_webenginequick, Path(cls.temp_dir) / "nanobrowser")
+        ).resolve()
+
     # this test case retains the QtWebEngine dlls
     def testWebEngineQuickDryRun(self):
         # setup
@@ -203,7 +262,7 @@ class TestPySide6Deploy(unittest.TestCase):
         expected_run_cmd = (
             f"{sys.executable} -m nuitka {str(main_file)} --follow-imports --onefile"
             f" --enable-plugin=pyside6 --output-dir={str(deployment_files)} --quiet"
-            f" --noinclude-qt-translations=True --include-qt-plugins=all"
+            f" --noinclude-qt-translations --include-qt-plugins=all"
             f" {data_files_cmd}"
         )
 
@@ -212,16 +271,20 @@ class TestPySide6Deploy(unittest.TestCase):
                 " --noinclude-dlls=libQt6Charts*"
                 " --noinclude-dlls=libQt6Quick3D* --noinclude-dlls=libQt6Sensors*"
                 " --noinclude-dlls=libQt6Test*"
-                )
+            )
         else:
             expected_run_cmd += (
                 " --noinclude-dlls=Qt6Charts*"
                 " --noinclude-dlls=Qt6Quick3D* --noinclude-dlls=Qt6Sensors*"
                 " --noinclude-dlls=Qt6Test*"
-                )
+            )
 
         if sys.platform.startswith("linux"):
-            expected_run_cmd += f" --linux-onefile-icon={str(self.linux_onefile_icon)}"
+            expected_run_cmd += f" --linux-icon={str(self.linux_icon)}"
+        elif sys.platform == "darwin":
+            expected_run_cmd += f" --macos-app-icon={str(self.macos_icon)}"
+        elif sys.platform == "win32":
+            expected_run_cmd += f" --windows-icon-from-ico={str(self.win_icon)}"
 
         config_file = self.temp_example_webenginequick / "pysidedeploy.spec"
 
@@ -237,7 +300,7 @@ class TestPySide6Deploy(unittest.TestCase):
             self.assertEqual(mock_qmlimportscanner.call_count, 2)
 
         # test config file contents
-        config_obj = ConfigFile(config_file=config_file)
+        config_obj = self.deploy_lib.BaseConfig(config_file=config_file)
         self.assertEqual(config_obj.get_value("app", "input_file"), "quicknanobrowser.py")
         self.assertEqual(config_obj.get_value("qt", "qml_files"), ",".join(qml_files))
         self.assertEqual(
@@ -245,14 +308,6 @@ class TestPySide6Deploy(unittest.TestCase):
             "QtCharts,QtQuick3D,QtSensors,QtTest",
         )
 
-    def tearDown(self) -> None:
-        super().tearDown()
-        os.chdir(self.current_dir)
-
-    @classmethod
-    def tearDownClass(cls) -> None:
-        shutil.rmtree(Path(cls.temp_dir))
-
 
 if __name__ == "__main__":
     unittest.main()
index 5a242160935db7b27934bcb22ec7181ca1a853a8..cfb665640afa279d38754c205a1665e10ced51f5 100644 (file)
@@ -83,9 +83,9 @@ if __name__ == '__main__':
         doc_filter = lambda x: x.startswith('test')
         doc_suffix = 'suffix'
 
-    assert(Implementing.testBase.__doc__ == 'prefixbasesuffix')
-    assert(Implementing.testWithoutDoc.__doc__ == None)
-    assert(OnlySuffix.testBase.__doc__ == 'basesuffix')
-    assert(OnlySuffix.testWithoutDoc.__doc__ == None)
-    assert(OnlyPrefix.testBase.__doc__ == 'prefixbase')
-    assert(OnlyPrefix.testWithoutDoc.__doc__ == None)
+    assert (Implementing.testBase.__doc__ == 'prefixbasesuffix')
+    assert (Implementing.testWithoutDoc.__doc__ == None)
+    assert (OnlySuffix.testBase.__doc__ == 'basesuffix')
+    assert (OnlySuffix.testWithoutDoc.__doc__ == None)
+    assert (OnlyPrefix.testBase.__doc__ == 'prefixbase')
+    assert (OnlyPrefix.testWithoutDoc.__doc__ == None)
index d6d070c695b439486b7189f6c0ee6fb2fbf041ed..f62e320f4b684f61c60d8f27df48149b125f3f2d 100644 (file)
@@ -12,6 +12,7 @@ import unittest
 # This should work with every compatible library.
 # Replaces the QtGui and QtCore versions as well.
 
+
 class UsesQApplication(unittest.TestCase):
     '''Helper class to provide Q(Core|Gui|)Application instances
     Just connect or call self.exit_app_cb. When called, will ask
index fb38943c81afa62ca5d9bb9afc51330f90796212..e471cf16779e89351926863a46f8bd1f02351943 100644 (file)
@@ -27,7 +27,7 @@ class ProcessTimer(object):
 
     def waitfor(self):
         time_passed = 0
-        while(self.proc.poll() is None and time_passed < self.timeout):
+        while (self.proc.poll() is None and time_passed < self.timeout):
             time_passed = time_passed + 1
             time.sleep(1)
 
index 7a0050783eb11fb498e4ceb6f6f79f014b388de1..836ed30568d095a5ef217f58060a1a806d0e4914 100644 (file)
@@ -1,5 +1,5 @@
 set(shiboken_MAJOR_VERSION "6")
 set(shiboken_MINOR_VERSION "6")
-set(shiboken_MICRO_VERSION "1")
+set(shiboken_MICRO_VERSION "2")
 set(shiboken_PRE_RELEASE_VERSION_TYPE "")
 set(shiboken_PRE_RELEASE_VERSION "")
index 53be95ca8dc3425fee3e7253337d070077ac5c5e..e922f8d5f04fce3e2ce6c39902f64a21f9eec12b 100644 (file)
@@ -2162,7 +2162,8 @@ AbstractMetaFunction *AbstractMetaBuilderPrivate::traverseFunction(const Functio
                 if (!currentClass || currentClass->typeEntry()->generateCode()) {
                     const QString signature = qualifiedFunctionSignatureWithType(functionItem, className);
                     qCWarning(lcShiboken, "%s",
-                              qPrintable(msgStrippingArgument(functionItem, i, signature, arg)));
+                              qPrintable(msgStrippingArgument(functionItem, i, signature,
+                                                              arg, errorMessage)));
                 }
                 break;
             }
index 23a44248ed44e856aeb309bd8d3fe2bab622fe58..6bcf5ea9adffd5d98fac2619fcacab130e293126 100644 (file)
@@ -975,12 +975,14 @@ QString AbstractMetaFunctionPrivate::formatMinimalSignature(const AbstractMetaFu
 {
     QString result = m_originalName + u'(';
     for (qsizetype i = 0; i < m_arguments.size(); ++i) {
+        const auto &argument = m_arguments.at(i);
         if (i > 0)
             result += u',';
 
-        result += comment
-            ? m_arguments.at(i).modifiedType().minimalSignature()
-            : m_arguments.at(i).type().minimalSignature();
+        const auto &type = comment ? argument.modifiedType() : argument.type();
+        result += type.minimalSignature();
+        if (comment && argument.hasDefaultValueExpression())
+            result += u'=';
     }
     result += u')';
     if (m_constant)
index ce0725378298dea29d9755ff57cd25212b38575d..87e215c8c38187930cd8ae9e1a927c436f94ef90 100644 (file)
@@ -1446,6 +1446,11 @@ void AbstractMetaClassPrivate::addUsingConstructors(const AbstractMetaClassPtr &
     }
 }
 
+static inline bool isSignal(const AbstractMetaFunctionCPtr &f)
+{
+    return f->isSignal();
+}
+
 void AbstractMetaClass::fixFunctions(const AbstractMetaClassPtr &klass)
 {
     auto *d = klass->d.data();
@@ -1482,6 +1487,10 @@ void AbstractMetaClass::fixFunctions(const AbstractMetaClassPtr &klass)
             *superClass -= AbstractMetaClass::FinalInTargetLang;
         }
         superFuncs = superClass->queryFunctions(FunctionQueryOption::ClassImplements);
+        // We are not interested in signals as no bindings are generated for them;
+        // they cause documentation warnings.
+        superFuncs.erase(std::remove_if(superFuncs.begin(), superFuncs.end(), isSignal),
+                         superFuncs.end());
         const auto virtuals = superClass->queryFunctions(FunctionQueryOption::VirtualInCppFunctions);
         superFuncs += virtuals;
 
index e913e10f1a2a7f330bff6eb32a289094ef0163ad..d9f5bde4106fa43e42ea5b61555eb66226113a94 100644 (file)
@@ -7,6 +7,8 @@
 #include "messages.h"
 #include "typedatabase.h"
 #include "containertypeentry.h"
+#include "enumtypeentry.h"
+#include "flagstypeentry.h"
 
 #include "qtcompat.h"
 #include "typeinfo.h"
@@ -698,10 +700,12 @@ QString AbstractMetaTypeData::formatPythonSignature() const
     if (m_typeEntry->isPrimitive())
         for (Indirection i : m_indirections)
             result += TypeInfo::indirectionKeyword(i);
-    // If it is a flags type, we replace it with the full name:
-    // "PySide6.QtCore.Qt.ItemFlags" instead of "PySide6.QtCore.QFlags<Qt.ItemFlag>"
-    if (m_typeEntry->isFlags())
-        result = m_typeEntry->qualifiedTargetLangName();
+    // If it is a flags type, we replace it with the full name of the enum:
+    // "PySide6.QtCore.Qt.ItemFlag" instead of "PySide6.QtCore.QFlags<Qt.ItemFlag>"
+    if (m_typeEntry->isFlags()) {
+        const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(m_typeEntry);
+        result = fte->originator()->qualifiedTargetLangName();
+    }
     result.replace(u"::"_s, u"."_s);
     return result;
 }
index b37f73098cd13aca0fb0bd7e502a9b39adb716c1..dd3d96bf8ab7d51a71998e64b3d744da6f599e97 100644 (file)
@@ -10,6 +10,7 @@
 #include "typedatabase_typedefs.h"
 
 #include <QtCore/qobjectdefs.h>
+#include <QtCore/QHashFunctions>
 #include <QtCore/QSharedDataPointer>
 #include <QtCore/QList>
 #include <QtCore/QSet>
@@ -253,6 +254,9 @@ public:
 #endif
 
 private:
+    friend size_t qHash(const AbstractMetaType &t, size_t seed = 0) noexcept
+    { return qHash(t.typeEntry().get(), seed); }
+
     friend class AbstractMetaTypeData;
     QSharedDataPointer<AbstractMetaTypeData> d;
 
@@ -266,9 +270,6 @@ inline bool operator==(const AbstractMetaType &t1, const AbstractMetaType &t2)
 inline bool operator!=(const AbstractMetaType &t1, const AbstractMetaType &t2)
 { return !t1.equals(t2); }
 
-inline size_t qHash(const AbstractMetaType &t, size_t seed)
-{ return qHash(t.typeEntry().get(), seed); }
-
 #ifndef QT_NO_DEBUG_STREAM
 QDebug operator<<(QDebug d, const AbstractMetaType &at);
 QDebug operator<<(QDebug d, const AbstractMetaType *at);
index 68b10d5b4575874e78dddcd0d20295f132c689c9..4e1f1ab632049c39088f0f58a36c43701c683040 100644 (file)
 
 #include <string_view>
 
-bool operator==(const CXCursor &c1, const CXCursor &c2)
+bool operator==(const CXCursor &c1, const CXCursor &c2) noexcept
 {
     return c1.kind == c2.kind
         && c1.xdata == c2.xdata
         && std::equal(c1.data, c1.data + sizeof(c1.data) / sizeof(c1.data[0]), c2.data);
 }
 
-size_t qHash(const CXCursor &c, size_t seed)
+size_t qHash(const CXCursor &c, size_t seed) noexcept
 {
-    return qHash(c.kind) ^ qHash(c.xdata) ^ qHash(c.data[0])
-        ^ qHash(c.data[1])  ^ qHash(c.data[2]) ^ seed;
+    return qHashMulti(seed, c.kind, c.xdata, c.data[0], c.data[1], c.data[2]);
 }
 
-bool operator==(const CXType &t1, const CXType &t2)
+bool operator==(const CXType &t1, const CXType &t2) noexcept
 {
     return t1.kind == t2.kind && t1.data[0] == t2.data[0]
         && t1.data[1] == t2.data[1];
 }
 
-size_t qHash(const CXType &ct, size_t seed)
+size_t qHash(const CXType &ct, size_t seed) noexcept
 {
-    return size_t(ct.kind) ^ size_t(0xFFFFFFFF & quintptr(ct.data[0]))
-        ^ size_t(0xFFFFFFFF & quintptr(ct.data[1])) ^ seed;
+    return qHashMulti(seed, ct.kind, ct.data[0], ct.data[1]);
 }
 
 namespace clang {
index 4ad21eebe6c7eb6e13a10294fdde646b4cfcf0b0..eaa04daed7e5f46154b60f99790debb9b2a21074 100644 (file)
 
 QT_FORWARD_DECLARE_CLASS(QDebug)
 
-bool operator==(const CXCursor &c1, const CXCursor &c2);
-size_t qHash(const CXCursor &c, size_t seed = 0);
+bool operator==(const CXCursor &c1, const CXCursor &c2) noexcept;
+size_t qHash(const CXCursor &c, size_t seed = 0) noexcept;
 
-bool operator==(const CXType &t1, const CXType &t2);
-size_t qHash(const CXType &ct, size_t seed);
+bool operator==(const CXType &t1, const CXType &t2) noexcept;
+size_t qHash(const CXType &ct, size_t seed = 0) noexcept;
 
 namespace clang {
 
index 2fdc3d1f668b0bd6ebfc4055543cf54b7e3950bf..bcfbc39dbad12158f695d38e056e8078a83ef083 100644 (file)
@@ -119,7 +119,7 @@ void DoxygenParser::fillDocumentation(const AbstractMetaClassPtr &metaClass)
                      + func->originalName() + u"\"]"_s;
 
             if (func->arguments().isEmpty()) {
-                QString args = func->isConstant() ? u"() const "_s : u"()"_s;
+                QString args = func->isConstant() ? u"() const"_s : u"()"_s;
                 query += u"/../argsstring[text()=\""_s + args + u"\"]"_s;
             } else {
                 int i = 1;
index d2d577817790b1b48e895cd3593ee4509400ad50..38f6ce744c3bb1bfe4b34fb35b4dd772d59be34b 100644 (file)
@@ -33,11 +33,6 @@ int Include::compare(const Include &rhs) const
     return m_name.compare(rhs.m_name);
 }
 
-size_t qHash(const Include& inc)
-{
-    return qHash(inc.m_name);
-}
-
 QTextStream& operator<<(QTextStream& out, const Include& g)
 {
     if (g.isValid())
index 7fd7d3b365456c3c4f67a2a571647b223781d5f3..dcf8976c6655dab8616f3f8f7106b70608e01944 100644 (file)
@@ -4,6 +4,7 @@
 #ifndef INCLUDE_H
 #define INCLUDE_H
 
+#include <QtCore/QHashFunctions>
 #include <QtCore/QString>
 #include <QtCore/QList>
 
@@ -42,15 +43,17 @@ public:
 
     QString toString() const;
 
-    friend size_t qHash(const Include &);
     int compare(const Include &rhs) const;
 
-    private:
-        IncludeType m_type = IncludePath;
-        QString m_name;
-};
+private:
+    friend size_t qHash(Include &inc, size_t seed = 0) noexcept
+    {
+        return qHashMulti(seed, inc.m_type, inc.m_name);
+    }
 
-size_t qHash(const Include& inc);
+    IncludeType m_type = IncludePath;
+    QString m_name;
+};
 
 inline bool operator<(const Include &lhs, const Include &rhs)
 {
index e639345129c6dec959666c2b8cdee7f3a420a4af..766b05beb878e3bed0c5f38c33c523911c3ed91c 100644 (file)
@@ -377,14 +377,15 @@ QString msgGlobalFunctionNotDefined(const FunctionTypeEntryCPtr &fte,
 
 QString msgStrippingArgument(const FunctionModelItem &f, int i,
                              const QString &originalSignature,
-                             const ArgumentModelItem &arg)
+                             const ArgumentModelItem &arg,
+                             const QString &reason)
 {
     QString result;
     QTextStream str(&result);
     str << f->sourceLocation() << "Stripping argument #" << (i + 1) << " of "
         << originalSignature << " due to unmatched type \""
         << arg->type().toString() << "\" with default expression \""
-        << arg->defaultValueExpression() << "\".";
+        << arg->defaultValueExpression() << "\": " << reason;
     return result;
 }
 
@@ -861,6 +862,12 @@ QString msgCannotFindSnippet(const QString &file, const QString &snippetLabel)
     return result;
 }
 
+QString msgSnippetError(const QString &context, const char *what)
+{
+    return "Error processing code snippet of "_L1 + context
+           + ": "_L1 + QString::fromUtf8(what);
+}
+
 QString msgUnableToResolveTypedef(const QString &sourceType, const QString &sourceName)
 {
     QString result;
index d1198b8be01c2f807b865c729dba8e2fde3a9ce1..2899cbdfa232a12743c074fa77fdaab15e9fe6d3 100644 (file)
@@ -99,7 +99,8 @@ QString msgGlobalFunctionNotDefined(const FunctionTypeEntryCPtr &fte,
 
 QString msgStrippingArgument(const FunctionModelItem &f, int i,
                              const QString &originalSignature,
-                             const ArgumentModelItem &arg);
+                             const ArgumentModelItem &arg,
+                             const QString &reason);
 
 QString msgEnumNotDefined(const EnumTypeEntryCPtr &t);
 
@@ -208,6 +209,7 @@ QString msgIncorrectlyNestedName(const QString &name);
 QString msgCannotFindView(const QString &viewedName, const QString &name);
 
 QString msgCannotFindSnippet(const QString &file, const QString &snippetLabel);
+QString msgSnippetError(const QString &context, const char *what);
 QString msgUnableToResolveTypedef(const QString &sourceType, const QString &sourceName);
 
 QString msgCyclicDependency(const QString &funcName, const QString &graphName,
index b71f01eb05773e85b4dace920d93d03e2c047842..1a46e5b265b9694f18d4cab505d24592fb0999cd 100644 (file)
@@ -15,7 +15,7 @@ endmacro()
 macro(set_debug_build)
     set(SHIBOKEN_BUILD_TYPE "Debug")
 
-    if(NOT PYTHON_DEBUG_LIBRARIES)
+    if(NOT Python_LIBRARIES)
         message(WARNING "Python debug shared library not found; \
             assuming python was built with shared library support disabled.")
     endif()
@@ -116,7 +116,7 @@ macro(shiboken_internal_set_python_site_packages)
         endif()
     else()
         execute_process(
-            COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+            COMMAND ${Python_EXECUTABLE} -c "if True:
                 import sysconfig
                 from os.path import sep
 
@@ -202,6 +202,12 @@ macro(get_python_extension_suffix)
     # Python_SOABI is only set by CMake 3.17+
     # TODO: Lower this to CMake 3.16 if possible.
     if(SHIBOKEN_IS_CROSS_BUILD)
+        # For android platform armv7a FindPython module return Python_SOABI as empty because
+        # it is unable to set Python_CONFIG i.e. find `python3-config` script
+        # This workaround sets the Python_SOABI manually for this platform.
+        if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a")
+            set(Python_SOABI "cpython-310}")
+        endif()
         if(NOT Python_SOABI)
             message(FATAL_ERROR "Python_SOABI variable is empty.")
         endif()
@@ -209,7 +215,7 @@ macro(get_python_extension_suffix)
     else()
         # See PYSIDE-1841 / https://bugs.python.org/issue39825 for distutils vs sysconfig
         execute_process(
-          COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+          COMMAND ${Python_EXECUTABLE} -c "if True:
              import sys
              if sys.version_info >= (3, 8, 2):
                  import sysconfig
@@ -241,18 +247,22 @@ macro(shiboken_check_if_limited_api)
     # TODO: Figure out how to use limited API libs when cross-building to Windows, if that's ever
     # needed. Perhaps use host python to walk the libs of the target python installation.
 
-    if(NOT SHIBOKEN_IS_CROSS_BUILD)
+    if(NOT SHIBOKEN_IS_CROSS_BUILD AND WIN32)
         # On Windows, PYTHON_LIBRARIES can be a list. Example:
         #    optimized;C:/Python36/libs/python36.lib;debug;C:/Python36/libs/python36_d.lib
         # On other platforms, this result is not used at all.
         execute_process(
-            COMMAND ${PYTHON_EXECUTABLE} -c "if True:
-                import os
-                for lib in '${PYTHON_LIBRARIES}'.split(';'):
-                    if '/' in lib and os.path.isfile(lib):
-                        prefix, py = lib.rsplit('/', 1)
+            COMMAND ${Python_EXECUTABLE} -c "if True:
+                from pathlib import Path
+                libs = r'${Python_LIBRARIES}'
+                libs = libs.split(';')
+                for lib in libs:
+                    if '\\\\' in lib and Path(lib).is_file():
+                        lib = Path(lib)
+                        prefix = lib.parent
+                        py = lib.name
                         if py.startswith('python3'):
-                            print(prefix + '/python3.lib')
+                            print(prefix / 'python3.lib')
                             break
                 "
             OUTPUT_VARIABLE PYTHON_LIMITED_LIBRARIES
@@ -271,6 +281,10 @@ endmacro()
 
 
 macro(shiboken_find_required_python)
+    set(_shiboken_find_python_version_args "")
+    if(${ARGC} GREATER 0)
+        list(APPEND _shiboken_find_python_version_args "${ARGV0}")
+    endif()
     # This function can also be called by consumers of ShibokenConfig.cmake package like pyside,
     # that's why we also check for PYSIDE_IS_CROSS_BUILD (which is set by pyside project)
     # and QFP_FIND_NEW_PYTHON_PACKAGE for an explicit opt in.
@@ -278,11 +292,6 @@ macro(shiboken_find_required_python)
     # We have to use FindPython package instead of FindPythonInterp to get required target Python
     # information.
     if(SHIBOKEN_IS_CROSS_BUILD OR PYSIDE_IS_CROSS_BUILD OR QFP_FIND_NEW_PYTHON_PACKAGE)
-        set(_shiboken_find_python_version_args "")
-        if(${ARGC} GREATER 0)
-            list(APPEND _shiboken_find_python_version_args "${ARGV0}")
-        endif()
-
         # We want FindPython to look in the sysroot for the python-config executable,
         # but toolchain files might set CMAKE_FIND_ROOT_PATH_MODE_PROGRAM to NEVER because
         # programs are mostly found for running and you usually can't run a target executable on
@@ -311,40 +320,23 @@ macro(shiboken_find_required_python)
             "${_shiboken_backup_CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}")
         set(CMAKE_FIND_ROOT_PATH
             "${_shiboken_backup_CMAKE_FIND_ROOT_PATH}")
-
-        # Mirror the variables that FindPythonInterp sets, instead of conditionally checking
-        # and modifying all the places where the variables are used.
-        set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}")
-        set(PYTHON_VERSION "${Python_VERSION}")
-        set(PYTHON_LIBRARIES "${Python_LIBRARIES}")
-        set(PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}")
-        set(PYTHONINTERP_FOUND "${Python_Interpreter_FOUND}")
-        set(PYTHONINTERP_FOUND "${Python_Interpreter_FOUND}")
-        set(PYTHONLIBS_FOUND "${Python_Development_FOUND}")
-        set(PYTHON_VERSION_MAJOR "${Python_VERSION_MAJOR}")
-        set(PYTHON_VERSION_MINOR "${Python_VERSION_MINOR}")
-        set(PYTHON_VERSION_PATCH "${Python_VERSION_PATCH}")
     else()
-        if(${ARGC} GREATER 0)
-            find_package(PythonInterp ${ARGV0} REQUIRED)
-            find_package(PythonLibs ${ARGV0} REQUIRED)
-        else()
-            # If no version is specified, just use any interpreter that can be found (from PATH).
-            # This is useful for super-project builds, so that the default system interpeter
-            # gets picked up (e.g. /usr/bin/python and not /usr/bin/python2.7).
-            find_package(PythonInterp REQUIRED)
-            find_package(PythonLibs REQUIRED)
-        endif()
+        find_package(
+            Python
+            ${_shiboken_find_python_version_args}
+            REQUIRED
+            COMPONENTS Interpreter Development
+        )
     endif()
 
     shiboken_validate_python_version()
 
-    set(SHIBOKEN_PYTHON_INTERPRETER "${PYTHON_EXECUTABLE}")
-    set_property(GLOBAL PROPERTY SHIBOKEN_PYTHON_INTERPRETER "${PYTHON_EXECUTABLE}")
+    set(SHIBOKEN_PYTHON_INTERPRETER "${Python_EXECUTABLE}")
+    set_property(GLOBAL PROPERTY SHIBOKEN_PYTHON_INTERPRETER "${Python_EXECUTABLE}")
 endmacro()
 
 macro(shiboken_validate_python_version)
-    if(PYTHON_VERSION_MAJOR EQUAL "3" AND PYTHON_VERSION_MINOR LESS "7")
+    if(Python_VERSION_MAJOR EQUAL "3" AND Python_VERSION_MINOR LESS "7")
             message(FATAL_ERROR
                    "Shiboken requires Python 3.7+.")
     endif()
@@ -365,14 +357,14 @@ macro(shiboken_compute_python_includes)
     if (SHIBOKEN_COMPUTE_INCLUDES_IS_CALLED_FROM_EXPORT)
         #TODO target_include_directories works on imported targets only starting with v3.11.0.
         set_property(TARGET Shiboken6::libshiboken
-                     APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS})
+                     APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Python_INCLUDE_DIRS})
     else()
         target_include_directories(libshiboken
-                                   PUBLIC $<BUILD_INTERFACE:${PYTHON_INCLUDE_DIRS}>)
+                                   PUBLIC $<BUILD_INTERFACE:${Python_INCLUDE_DIRS}>)
     endif()
 
 
-    set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIRS}")
+    set(SHIBOKEN_PYTHON_INCLUDE_DIRS "${Python_INCLUDE_DIRS}")
 
     set_property(GLOBAL PROPERTY shiboken_python_include_dirs "${SHIBOKEN_PYTHON_INCLUDE_DIRS}")
 
@@ -432,16 +424,8 @@ macro(shiboken_compute_python_libraries)
         set(SHIBOKEN_PYTHON_LIBRARIES "")
     endif()
 
-    if(CMAKE_BUILD_TYPE STREQUAL "Debug")
-        if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES)
-            set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_DEBUG_LIBRARIES})
-        endif()
-    endif()
-
-    if(CMAKE_BUILD_TYPE STREQUAL "Release")
-        if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES)
-            set(SHIBOKEN_PYTHON_LIBRARIES ${PYTHON_LIBRARIES})
-        endif()
+    if(WIN32 AND NOT SHIBOKEN_PYTHON_LIBRARIES)
+        set(SHIBOKEN_PYTHON_LIBRARIES ${Python_LIBRARIES})
     endif()
 
     # If the resulting variable
@@ -474,20 +458,20 @@ macro(shiboken_compute_python_libraries)
 endmacro()
 
 function(shiboken_check_if_built_and_target_python_are_compatible)
-    if(NOT SHIBOKEN_PYTHON_VERSION_MAJOR STREQUAL PYTHON_VERSION_MAJOR)
+    if(NOT SHIBOKEN_PYTHON_VERSION_MAJOR STREQUAL Python_VERSION_MAJOR)
         message(FATAL_ERROR "The detected Python major version is not \
 compatible with the Python major version which was used when Shiboken was built.
 Built with: '${SHIBOKEN_PYTHON_VERSION_MAJOR}.${SHIBOKEN_PYTHON_VERSION_MINOR}' \
-Detected: '${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}'")
+Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'")
     else()
         if(NOT SHIBOKEN_PYTHON_LIMITED_API
-           AND NOT SHIBOKEN_PYTHON_VERSION_MINOR STREQUAL PYTHON_VERSION_MINOR)
+           AND NOT SHIBOKEN_PYTHON_VERSION_MINOR STREQUAL Python_VERSION_MINOR)
             message(FATAL_ERROR
                     "The detected Python minor version is not compatible with the Python minor \
 version which was used when Shiboken was built. Consider building shiboken with \
 FORCE_LIMITED_API set to '1', so that only the Python major version matters.
 Built with: '${SHIBOKEN_PYTHON_VERSION_MAJOR}.${SHIBOKEN_PYTHON_VERSION_MINOR}' \
-Detected: '${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}'")
+Detected: '${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}'")
         endif()
     endif()
 endfunction()
@@ -690,28 +674,10 @@ endmacro()
 # tool_name should be a unique tool name, preferably without spaces.
 # Returns the wrapper path in path_out_var.
 #
-# Currently adds the Qt lib dir and libclang to PATH.
+# Currently adds the Qt lib dir and libclang to PATH / LD_LIBRARY_PATH / DYLD_LIBRARY_PATH.
 # Meant to be used as the first argument to add_custom_command's COMMAND option.
 # TODO: Remove tool_name as the tool_name for this function is always shiboken.
 function(shiboken_get_tool_shell_wrapper tool_name path_out_var)
-
-    # Make sure that for cross building, the host shiboken_wrapper.sh tool is used instead of target
-    # shiboken_wrapper.sh when calling shiboken. This wrapper script resolves the dependency to Qt
-    # libraries.
-    if((SHIBOKEN_IS_CROSS_BUILD OR PYSIDE_IS_CROSS_BUILD) AND
-       (CMAKE_HOST_UNIX AND NOT CMAKE_HOST_APPLE))
-        set(host_tool_wrapper_path
-            "${QFP_SHIBOKEN_HOST_PATH}/../build/shiboken6/.qfp/bin/shiboken_wrapper.sh")
-
-        if(EXISTS "${host_tool_wrapper_path}")
-            set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_shiboken_path"
-                        "${host_tool_wrapper_path}")
-            set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_shiboken_created" TRUE)
-        else()
-            message(FATAL_ERROR "${host_tool_wrapper_path} does not exist")
-        endif()
-    endif()
-
     # Generate the wrapper only once during the execution of CMake.
     get_property(is_called GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_created")
 
@@ -725,25 +691,56 @@ function(shiboken_get_tool_shell_wrapper tool_name path_out_var)
     set(path_dirs_native "")
 
     if(CMAKE_HOST_WIN32)
+        set(wrapper_script_extension ".bat")
+    else()
+        set(wrapper_script_extension ".sh")
+    endif()
+
+    # Try to get original host shiboken paths from exported target properties.
+    shiboken_get_host_tool_wrapper_properties(orig_qt_library_dir_absolute orig_libclang_lib_dir)
+
+    # Get path to the Qt bin/lib dir depending on the platform and developer input.
+    # Prefer values given on the command line, then the original host path if it exists, otherwise
+    # try to use the Qt install prefix and libclang env vars.
+    #
+    # Note that in a cross-compiling case, using the Qt install prefix is very likely
+    # wrong, because you want to use the location of the host Qt, not the target Qt. Same for
+    # libclang. Unfortunately we currently don't provide a host Qt and host libclang option via
+    # setup.py, so the manual cmake vars will have to suffice.
+    if(SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH AND EXISTS "${SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH}")
+        set(qt_library_dir_absolute "${SHIBOKEN_WRAPPER_HOST_QT_LIB_PATH}")
+    elseif(orig_qt_library_dir_absolute AND EXISTS "${orig_qt_library_dir_absolute}")
+        set(qt_library_dir_absolute "${orig_qt_library_dir_absolute}")
+    elseif(CMAKE_HOST_WIN32)
         # in Windows the Qt dll are store `bin` in directory
         set(qt_library_dir ${QT6_INSTALL_BINS})
-        set(wrapper_script_extension ".bat")
     else()
         # in Unix the .so are stored in `lib` directory
         set(qt_library_dir ${QT6_INSTALL_LIBS})
-        set(wrapper_script_extension ".sh")
     endif()
 
     # Assert that Qt is already found.
-    if(NOT QT6_INSTALL_PREFIX OR NOT qt_library_dir)
+    if((QT6_INSTALL_PREFIX AND qt_library_dir) OR orig_qt_library_dir_absolute)
+    else()
         message(FATAL_ERROR "Qt should have been found already by now.")
     endif()
 
-    # Get path to the Qt bin/lib dir depending on the platform
-    list(APPEND path_dirs "${QT6_INSTALL_PREFIX}/${qt_library_dir}")
+    if(NOT qt_library_dir_absolute)
+        set(qt_library_dir_absolute "${QT6_INSTALL_PREFIX}/${qt_library_dir}")
+    endif()
+    list(APPEND path_dirs "${qt_library_dir_absolute}")
+
+    # Get libclang lib dir path.
+    # Prefer values given on the command line, then the original host path if it exists.
+    if(SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH AND EXISTS "${SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH}")
+        set(libclang_lib_dir "${SHIBOKEN_WRAPPER_HOST_CLANG_LIB_PATH}")
+    elseif(orig_libclang_lib_dir AND EXISTS "${orig_libclang_lib_dir}")
+        set(libclang_lib_dir "${orig_libclang_lib_dir}")
+    else()
+        # find libclang
+        find_libclang()
+    endif()
 
-    # find libclang
-    find_libclang()
     if(libclang_lib_dir)
         list(APPEND path_dirs "${libclang_lib_dir}")
     endif()
@@ -781,6 +778,9 @@ $@")
     set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_path" "${wrapper_path}")
     set_property(GLOBAL PROPERTY "_shiboken_tool_wrapper_${tool_name}_created" TRUE)
 
+    # Save original host paths for future cross-builds.
+    shiboken_save_host_tool_wrapper_properties("${qt_library_dir_absolute}" "${libclang_lib_dir}")
+
     # give execute permission to run the file
     if(CMAKE_HOST_UNIX)
         execute_process(COMMAND chmod +x ${wrapper_path})
@@ -789,6 +789,48 @@ $@")
     set(${path_out_var} "${wrapper_path}" PARENT_SCOPE)
 endfunction()
 
+# Retrieve the original host shiboken runtime dependency paths from the installed (namespaced)
+# shiboken generator target.
+function(shiboken_get_host_tool_wrapper_properties out_qt_library_dir out_libclang_lib_dir)
+    if(TARGET Shiboken6::shiboken6)
+        get_target_property(qt_library_dir Shiboken6::shiboken6 _shiboken_original_qt_lib_dir)
+        if(NOT qt_library_dir)
+            set(qt_library_dir "")
+        endif()
+        get_target_property(libclang_lib_dir Shiboken6::shiboken6
+            _shiboken_original_libclang_lib_dir)
+        if(NOT libclang_lib_dir)
+            set(libclang_lib_dir "")
+        endif()
+    endif()
+
+    set(${out_qt_library_dir} "${qt_library_dir}" PARENT_SCOPE)
+    set(${out_libclang_lib_dir} "${libclang_lib_dir}" PARENT_SCOPE)
+endfunction()
+
+# Save original host shiboken runtime dependency paths as target properties, so they can be used
+# when generating the wrapper file for cross-builds.
+# Should only be done when shiboken is being built (aka it's a non-imported target).
+function(shiboken_save_host_tool_wrapper_properties qt_library_dir libclang_lib_dir)
+    if(TARGET shiboken6)
+        get_target_property(is_imported shiboken6 IMPORTED)
+        if(is_imported)
+            return()
+        endif()
+
+        set_target_properties(shiboken6 PROPERTIES
+            _shiboken_original_qt_lib_dir "${qt_library_dir}")
+        set_property(TARGET shiboken6 APPEND PROPERTY
+            EXPORT_PROPERTIES _shiboken_original_qt_lib_dir)
+        if(libclang_lib_dir)
+            set_target_properties(shiboken6 PROPERTIES
+                _shiboken_original_libclang_lib_dir "${libclang_lib_dir}")
+            set_property(TARGET shiboken6 APPEND PROPERTY
+                EXPORT_PROPERTIES _shiboken_original_libclang_lib_dir)
+        endif()
+    endif()
+endfunction()
+
 # Returns the platform-specific relative rpath base token, if it's supported.
 # If it's not supported, returns the string NO_KNOWN_RPATH_REL_BASE.
 function(get_rpath_base_token out_var)
index c1d477bef8360aee7d5b180321982104bbafccdc..32823d9fa66500ee19f5ee75eb9213d8bc6fb360 100644 (file)
@@ -7,7 +7,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
 
 include(ShibokenHelpers)
 
-option(USE_PYTHON_VERSION "Use specific python version to build shiboken6." "")
 option(DISABLE_DOCSTRINGS "Disable documentation extraction." FALSE)
 
 shiboken_internal_disable_pkg_config_if_needed()
@@ -44,13 +43,12 @@ set(shiboken6_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VER
 compute_config_py_values(shiboken6_VERSION)
 
 ## For debugging the PYTHON* variables
-message(STATUS "PYTHONLIBS_FOUND:       " ${PYTHONLIBS_FOUND})
-message(STATUS "PYTHON_LIBRARIES:       " ${PYTHON_LIBRARIES})
-message(STATUS "PYTHON_INCLUDE_DIRS:    " ${PYTHON_INCLUDE_DIRS})
-message(STATUS "PYTHON_DEBUG_LIBRARIES: " ${PYTHON_DEBUG_LIBRARIES})
-message(STATUS "PYTHONINTERP_FOUND:     " ${PYTHONINTERP_FOUND})
-message(STATUS "PYTHON_EXECUTABLE:      " ${PYTHON_EXECUTABLE})
-message(STATUS "PYTHON_VERSION:         " ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH})
+message(STATUS "Python_Development_FOUND:       " ${Python_Development_FOUND})
+message(STATUS "Python_LIBRARIES:       " ${Python_LIBRARIES})
+message(STATUS "Python_INCLUDE_DIRS:    " ${Python_INCLUDE_DIRS})
+message(STATUS "Python_Interpreter_FOUND:     " ${Python_Interpreter_FOUND})
+message(STATUS "Python_EXECUTABLE:      " ${Python_EXECUTABLE})
+message(STATUS "Python_VERSION:         " ${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}.${Python_VERSION_PATCH})
 
 if(NOT PYTHON_EXTENSION_SUFFIX)
     get_python_extension_suffix()
@@ -121,7 +119,7 @@ endif()
 # Detect if the python libs were compiled in debug mode
 # On Linux distros there is no standard way to check that.
 execute_process(
-    COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+    COMMAND ${Python_EXECUTABLE} -c "if True:
         import sys
         import sysconfig
         config_py_debug = sysconfig.get_config_var('Py_DEBUG')
@@ -139,7 +137,7 @@ if(SHIBOKEN_IS_CROSS_BUILD)
     set(PYTHON_WITH_COUNT_ALLOCS 0)
 else()
     execute_process(
-        COMMAND ${PYTHON_EXECUTABLE} -c "if True:
+        COMMAND ${Python_EXECUTABLE} -c "if True:
             count_allocs = False
             import sys
             try:
index a07563a536f2a8d505153944e36c76565a88a2fa..233404bc6b39686993fe741c322b3bab34dc23a6 100644 (file)
@@ -5,9 +5,9 @@
 
 # This is the version of Python against which Shiboken was built. Not necessarily the version
 # against which a downstream project is built (e.g. PySide6).
-set(SHIBOKEN_PYTHON_VERSION_MAJOR "@PYTHON_VERSION_MAJOR@")
-set(SHIBOKEN_PYTHON_VERSION_MINOR "@PYTHON_VERSION_MINOR@")
-set(SHIBOKEN_PYTHON_VERSION_PATCH "@PYTHON_VERSION_PATCH@")
+set(SHIBOKEN_PYTHON_VERSION_MAJOR "@Python_VERSION_MAJOR@")
+set(SHIBOKEN_PYTHON_VERSION_MINOR "@Python_VERSION_MINOR@")
+set(SHIBOKEN_PYTHON_VERSION_PATCH "@Python_VERSION_PATCH@")
 set(SHIBOKEN_PYTHON_LIMITED_API "@PYTHON_LIMITED_API@")
 
 # Import targets and call variable set up functions  only when using an installed shiboken config
@@ -17,7 +17,7 @@ if (NOT TARGET Shiboken6::libshiboken)
     include("${CMAKE_CURRENT_LIST_DIR}/ShibokenHelpers.cmake")
 
     # Compute the python include and libraries path if needed (aka not part of super project build).
-    shiboken_find_required_python(@PYTHON_VERSION_MAJOR@)
+    shiboken_find_required_python(@Python_VERSION_MAJOR@)
     shiboken_check_if_built_and_target_python_are_compatible()
     shiboken_check_if_limited_api()
     shiboken_compute_python_includes(IS_CALLED_FROM_EXPORT)
index 3ba422627486cd360a53283e7a56d78b2cf3e94e..a82d23168958f42e85d3359ce07881e0a32d07a9 100644 (file)
@@ -2,11 +2,11 @@ prefix=@CMAKE_INSTALL_PREFIX@
 exec_prefix=@CMAKE_INSTALL_PREFIX@
 libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@
 includedir=@CMAKE_INSTALL_PREFIX@/include/shiboken6
-python_interpreter=@PYTHON_EXECUTABLE@
-python_include_dir=@PYTHON_INCLUDE_DIRS@
+python_interpreter=@Python_EXECUTABLE@
+python_include_dir=@Python_INCLUDE_DIRS@
 
 Name: shiboken6
 Description: Support library for Python bindings created with the Shiboken6 generator.
 Version: @shiboken6_VERSION@
 Libs: @SHIBOKEN_PYTHON_LIBRARIES@ -L${libdir} -lshiboken6@shiboken6_SUFFIX@@PYTHON_SHARED_LIBRARY_SUFFIX@@LIBRARY_OUTPUT_SUFFIX@
-Cflags: -I@PYTHON_INCLUDE_DIRS@ -I${includedir}/@shiboken6_SUFFIX@@SBK_PKG_CONFIG_PY_DEBUG_DEFINITION@
+Cflags: -I@Python_INCLUDE_DIRS@ -I${includedir}/@shiboken6_SUFFIX@@SBK_PKG_CONFIG_PY_DEBUG_DEFINITION@
index 74987703d4a64e36d56b646a8ddaaa6c17c15071..eaef4ff29133bee9f140e5f049f6e4cdde372d32 100644 (file)
@@ -37,7 +37,7 @@ if(SPHINX_BUILD)
             if(SHIBOKEN_IS_CROSS_BUILD)
                 set(python_executable "${QFP_PYTHON_HOST_PATH}")
             else()
-                set(python_executable "${PYTHON_EXECUTABLE}")
+                set(python_executable "${Python_EXECUTABLE}")
             endif()
             if(NOT python_executable OR NOT EXISTS "${python_executable}")
                 message(FATAL_ERROR "No python executable found to build documentation.")
index 23bad0d738aebde0ebc453b4bab74e452dbc6ff3..b10f33b2aad7afb19e2bf0f723d8b2d3feb3535c 100644 (file)
@@ -73,7 +73,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'Shiboken'
-copyright = u'2021 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
+copyright = u'2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 (https://www.gnu.org/licenses/fdl.html) as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.'
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
index dde5fca625c9b77ab664821c76332edd5587dba6..14340ab69d217be0d25dbaf9ccd39c53ba99456d 100644 (file)
@@ -322,6 +322,9 @@ QtDocGenerator Options
 ``--inheritance-file=<file>``
    Generate a JSON file containing the class inheritance.
 
+``--disable-inheritance-diagram``
+        Disable the generation of the inheritance diagram.
+
 .. _project-file:
 
 ********************
index 7152c70af6da33f18cbecb3e124e6117b09b1f3c..406d51650892ca7093b2ea4751edec94ec9ec09f 100644 (file)
@@ -205,6 +205,8 @@ child nodes.
             generate-using="yes | no"
             package="..."
             since="..."
+            extends = "..."
+            files = "..."
             revision="..." />
     </typesystem>
 
@@ -235,6 +237,16 @@ The *optional* **since** value is used to specify the API version of this type.
 The **revision** attribute can be used to specify a revision for each type, easing the
 production of ABI compatible bindings.
 
+The *optional* **extends** attribute specifies the module name where the given
+namespace first occurs in case of a namespace spanning several modules. For
+example, in Qt, the namespace ``Qt`` first occurs in the ``QtCore`` module and
+is further populated in the ``QtGui`` module. ``QtGui.Qt`` will then be
+generated extending ``QtCore.Qt`` if **extends** is specified.
+
+The *optional* **file** attribute specifies a regular expression matching the
+include files whose contents are to be associated with the current module in
+case of a namespace spanning several modules.
+
 .. _enum-type:
 
 enum-type
index 466de43259908b9ec9fa7b33634ae3f3a56c9235..aebe2cd5eeff96c7431699684adb354473a4432c 100644 (file)
@@ -6,7 +6,7 @@ set(package_name "Shiboken6Tools")
 
 set(CMAKE_AUTOMOC ON)
 
-if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND PYTHONINTERP_FOUND))
+if(NOT (Qt${QT_MAJOR_VERSION}Core_FOUND AND Python_Interpreter_FOUND))
     message(WARNING "Some dependencies were not found: shiboken6 generator compilation disabled!")
     return()
 endif()
index 9043550b33f2cedb53863842d32e22e7633cc8dc..7785b8f9120126b1031d35a6e6cfdfc0bf56637f 100644 (file)
@@ -386,7 +386,7 @@ std::optional<DefaultValue>
     if (type.isNativePointer())
         return DefaultValue(DefaultValue::Pointer, type.typeEntry()->qualifiedCppName());
     if (type.isPointer())
-        return DefaultValue(DefaultValue::Pointer, u"::"_s + type.typeEntry()->qualifiedCppName());
+        return DefaultValue(DefaultValue::Pointer, getFullTypeName(type.typeEntry()));
 
     if (type.typeEntry()->isSmartPointer())
         return minimalConstructor(api, type.typeEntry());
@@ -435,8 +435,7 @@ std::optional<DefaultValue>
         if (const auto nullValue = enumEntry->nullValue())
             return DefaultValue(DefaultValue::Enum, nullValue->name());
         return DefaultValue(DefaultValue::Custom,
-                            u"static_cast< ::"_s + type->qualifiedCppName()
-                            + u">(0)"_s);
+                            "static_cast< "_L1 + getFullTypeName(type) + ">(0)"_L1);
     }
 
     if (type->isFlags()) {
index ef61a077b028d86766fd4fd5f460f7639a9d901b..bba60fa9a6011f53ebac796a68392506763a06cd 100644 (file)
@@ -15,6 +15,7 @@
 #include <abstractmetafield.h>
 #include <abstractmetafunction.h>
 #include <abstractmetalang.h>
+#include "abstractmetalang_helpers.h"
 #include <fileout.h>
 #include <messages.h>
 #include <modifications.h>
@@ -25,6 +26,8 @@
 #include <functiontypeentry.h>
 #include <enumtypeentry.h>
 #include <complextypeentry.h>
+#include <flagstypeentry.h>
+#include <primitivetypeentry.h>
 #include <qtdocparser.h>
 #include <doxygenparser.h>
 
@@ -36,6 +39,7 @@
 #include <QtCore/QJsonArray>
 #include <QtCore/QJsonDocument>
 #include <QtCore/QJsonObject>
+#include <QtCore/QSet>
 
 #include <algorithm>
 #include <limits>
@@ -49,6 +53,7 @@ struct DocGeneratorOptions
     QString additionalDocumentationList;
     QString inheritanceFile;
     bool doxygen = false;
+    bool inheritanceDiagram = true;
 };
 
 struct GeneratorDocumentation
@@ -288,6 +293,18 @@ void QtDocGenerator::writeFormattedText(TextStream &s, const QString &doc,
     s << '\n';
 }
 
+static void writeInheritanceList(TextStream &s, const AbstractMetaClassCList& classes,
+                                 const char *label)
+{
+    s << "**" << label << ":** ";
+    for (qsizetype i = 0, size = classes.size(); i < size; ++i) {
+        if (i > 0)
+            s << ", ";
+        s << ":ref:`" << classes.at(i)->name() << '`';
+    }
+    s << "\n\n";
+}
+
 static void writeInheritedByList(TextStream &s, const AbstractMetaClassCPtr &metaClass,
                                  const AbstractMetaClassCList& allClasses)
 {
@@ -297,14 +314,22 @@ static void writeInheritedByList(TextStream &s, const AbstractMetaClassCPtr &met
             res << c;
     }
 
-    if (res.isEmpty())
-        return;
+    if (!res.isEmpty())
+        writeInheritanceList(s, res, "Inherited by");
+}
+
+static void writeInheritedFromList(TextStream &s, const AbstractMetaClassCPtr &metaClass)
+{
+    AbstractMetaClassCList res;
 
-    s << "**Inherited by:** ";
-    QStringList classes;
-    for (const auto &c : std::as_const(res))
-        classes << u":ref:`"_s + c->name() + u'`';
-    s << classes.join(u", "_s) << "\n\n";
+    recurseClassHierarchy(metaClass, [&res, metaClass](const AbstractMetaClassCPtr &c) {
+        if (c.get() != metaClass.get())
+            res.append(c);
+        return false;
+    });
+
+    if (!res.isEmpty())
+        writeInheritanceList(s, res, "Inherits from");
 }
 
 void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classContext)
@@ -328,10 +353,14 @@ void QtDocGenerator::generateClass(TextStream &s, const GeneratorContext &classC
     if (documentation.hasBrief())
         writeFormattedBriefText(s, documentation, metaClass);
 
-    s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
-      << "    :parts: 2\n\n";
-    // TODO: This would be a parameter in the future...
-
+    if (!metaClass->baseClasses().isEmpty()) {
+        if (m_options.inheritanceDiagram) {
+            s << ".. inheritance-diagram:: " << metaClass->fullName()<< '\n'
+              << "    :parts: 2\n\n";
+        } else {
+            writeInheritedFromList(s, metaClass);
+        }
+    }
 
     writeInheritedByList(s, metaClass, api().classes());
 
@@ -681,6 +710,11 @@ QString QtDocGenerator::functionSignature(const AbstractMetaClassCPtr &cppClass,
     return funcName + formatArgs(func);
 }
 
+static QString inline toRef(const QString &t)
+{
+    return ":any:`"_L1 + t + u'`';
+}
+
 QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
                                               const AbstractMetaClassCPtr &cppClass,
                                               bool createRef) const
@@ -688,36 +722,47 @@ QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
     static const QStringList nativeTypes =
         {boolT(), floatT(), intT(), pyObjectT(), pyStrT()};
 
-    const QString name = type.name();
+    QString name = type.name();
     if (nativeTypes.contains(name))
         return name;
 
-    static const QMap<QString, QString> typeMap = {
+    if (type.typeUsagePattern() == AbstractMetaType::PrimitivePattern) {
+        const auto &basicName = basicReferencedTypeEntry(type.typeEntry())->name();
+        if (AbstractMetaType::cppSignedIntTypes().contains(basicName)
+            || AbstractMetaType::cppUnsignedIntTypes().contains(basicName)) {
+            return intT();
+        }
+        if (AbstractMetaType::cppFloatTypes().contains(basicName))
+            return floatT();
+    }
+
+    static const QSet<QString> stringTypes = {
+        u"uchar"_s, u"std::string"_s, u"std::wstring"_s,
+        u"std::stringview"_s, u"std::wstringview"_s,
+        qStringT(), u"QStringView"_s, u"QAnyStringView"_s, u"QUtf8StringView"_s
+    };
+    if (stringTypes.contains(name))
+        return pyStrT();
+
+    static const QHash<QString, QString> typeMap = {
         { cPyObjectT(), pyObjectT() },
-        { qStringT(), pyStrT() },
-        { u"uchar"_s, pyStrT() },
         { u"QStringList"_s, u"list of strings"_s },
-        { qVariantT(), pyObjectT() },
-        { u"quint32"_s, intT() },
-        { u"uint32_t"_s, intT() },
-        { u"quint64"_s, intT() },
-        { u"qint64"_s, intT() },
-        { u"size_t"_s, intT() },
-        { u"int64_t"_s, intT() },
-        { u"qreal"_s, floatT() }
+        { qVariantT(), pyObjectT() }
     };
-    const auto found = typeMap.find(name);
-    if (found != typeMap.end())
+    const auto found = typeMap.constFind(name);
+    if (found != typeMap.cend())
         return found.value();
 
-    QString strType;
-    if (type.isConstant() && name == u"char" && type.indirections() == 1) {
-        strType = u"str"_s;
-    } else if (name.startsWith(unsignedShortT())) {
-        strType = intT();
-    } else if (name.startsWith(unsignedT())) { // uint and ulong
-        strType = intT();
-    } else if (type.isContainer()) {
+    if (type.isFlags()) {
+        const auto fte = std::static_pointer_cast<const FlagsTypeEntry>(type.typeEntry());
+        auto enumName = fte->originator()->targetLangName();
+        return "Combination of "_L1 + (createRef ? toRef(enumName) : enumName);
+    }
+
+    if (type.isConstant() && name == "char"_L1 && type.indirections() == 1)
+        return "str"_L1;
+
+    if (type.isContainer()) {
         QString strType = translateType(type, cppClass, Options(ExcludeConst) | ExcludeReference);
         strType.remove(u'*');
         strType.remove(u'>');
@@ -733,15 +778,13 @@ QString QtDocGenerator::translateToPythonType(const AbstractMetaType &type,
             strType = QString::fromLatin1("Dictionary with keys of type %1 and values of type %2.")
                                          .arg(types[0], types[1]);
         }
-    } else {
-        auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry());
-        strType = k ? k->fullName() : type.name();
-        if (createRef) {
-            strType.prepend(u":any:`"_s);
-            strType.append(u'`');
-        }
+        return strType;
     }
-    return strType;
+
+    if (auto k = AbstractMetaClass::findClass(api().classes(), type.typeEntry()))
+        return createRef ? toRef(k->fullName()) : k->fullName();
+
+    return createRef ? toRef(name) : name;
 }
 
 QString QtDocGenerator::getFuncName(const AbstractMetaFunctionCPtr &cppFunc)
@@ -1123,7 +1166,9 @@ QList<OptionDescription> QtDocGenerator::options()
          u"List of additional XML files to be converted to .rst files\n"
           "(for example, tutorials)."_s},
         {u"inheritance-file=<file>"_s,
-         u"Generate a JSON file containing the class inheritance."_s}
+         u"Generate a JSON file containing the class inheritance."_s},
+        {u"disable-inheritance-diagram"_s,
+         u"Disable the generation of the inheritance diagram."_s}
     };
 }
 
@@ -1132,12 +1177,22 @@ class QtDocGeneratorOptionsParser : public OptionsParser
 public:
     explicit QtDocGeneratorOptionsParser(DocGeneratorOptions *o) : m_options(o) {}
 
+    bool handleBoolOption(const QString &key, OptionSource source) override;
     bool handleOption(const QString &key, const QString &value, OptionSource source) override;
 
 private:
     DocGeneratorOptions *m_options;
 };
 
+bool QtDocGeneratorOptionsParser::handleBoolOption(const QString &key, OptionSource)
+{
+    if (key == "disable-inheritance-diagram"_L1) {
+        m_options->inheritanceDiagram = false;
+        return true;
+    }
+    return false;
+}
+
 bool QtDocGeneratorOptionsParser::handleOption(const QString &key, const QString &value,
                                                OptionSource source)
 {
index 2fd01621826ae636b091db79faf29b5769a9182e..b129a453e93f4ccf0d3edfdc0b369962ba953888 100644 (file)
@@ -945,7 +945,7 @@ void QtXmlToSphinx::handleSnippetTag(QXmlStreamReader& reader)
             || m_lastTagName == u"dots" || m_lastTagName == u"codeline";
         if (consecutiveSnippet) {
             m_output.flush();
-            m_output.string()->chop(2);
+            m_output.string()->chop(1); // Strip newline from previous snippet
         }
         QString location = reader.attributes().value(u"location"_s).toString();
         QString identifier = reader.attributes().value(u"identifier"_s).toString();
index 74a9d8e915b20a067bdf7e60f3a643a50c7bea84..1f3c68ddd661e2f7443c664f038dd5c1cd6862d7 100644 (file)
@@ -455,6 +455,19 @@ void CppGenerator::writePyMethodDefs(TextStream &s, const QString &className,
         << methodsDefinitions << METHOD_DEF_SENTINEL << outdent << "};\n\n";
 }
 
+void CppGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+                                        TypeSystem::CodeSnipPosition position,
+                                        TypeSystem::Language language) const
+{
+    if (!codeSnips.isEmpty()) {
+        try {
+            writeCodeSnips(s, codeSnips, position, language);
+        } catch (const std::exception &e) {
+            throw Exception(msgSnippetError("module source of "_L1 + moduleName(), e.what()));
+        }
+    }
+}
+
 bool CppGenerator::hasHashFunction(const AbstractMetaClassCPtr &c)
 {
     return !c->typeEntry()->hashFunction().isEmpty()
@@ -1338,7 +1351,7 @@ void CppGenerator::writeVirtualMethodNative(TextStream &s,
             if (isProtectedEnum) {
                 QString typeCast;
                 if (metaEnum->enclosingClass())
-                    typeCast += u"::"_s + metaEnum->enclosingClass()->qualifiedCppName();
+                    typeCast += getFullTypeName(metaEnum->enclosingClass());
                 typeCast += u"::"_s + metaEnum->name();
                 s << '(' << typeCast << ')';
             }
@@ -1430,7 +1443,8 @@ static void generateDeprecatedValueWarnings(TextStream &c,
             << "\", \"" << v.name() << "\");\nbreak;\n" << outdent;
     }
     if (deprecatedValues.size() < metaEnum.values().size())
-        c << "default:\n" << indent << "break;\n" << outdent << "}\n";
+        c << "default:\n" << indent << "break;\n" << outdent;
+    c << "}\n";
 }
 
 void CppGenerator::writeEnumConverterFunctions(TextStream &s, const AbstractMetaEnum &metaEnum) const
@@ -2295,8 +2309,7 @@ void CppGenerator::writeCppSelfDefinition(TextStream &s,
         && cppWrapper.testFlag(AbstractMetaClass::CppProtectedHackWrapper);
     Q_ASSERT(!useWrapperClass || context.useWrapper());
     const QString className = useWrapperClass
-        ? context.wrapperName()
-        : (u"::"_s + metaClass->qualifiedCppName());
+        ? context.wrapperName() : getFullTypeName(metaClass);
 
     writeInvalidPyObjectCheck(s, u"self"_s, errorReturn);
 
@@ -3097,9 +3110,10 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, const QString &code,
 {
 
     QString prettyCode = code;
-    processCodeSnip(prettyCode);
+    const QString funcName = cppToPythonFunctionName(sourceTypeName, targetTypeName);
+    processCodeSnip(prettyCode, funcName);
 
-    s << "static PyObject *" << cppToPythonFunctionName(sourceTypeName, targetTypeName)
+    s << "static PyObject *" << funcName
         << "(const void *cppIn)\n{\n" << indent << prettyCode
         << ensureEndl << outdent << "}\n";
 }
@@ -3170,7 +3184,7 @@ void CppGenerator::writeCppToPythonFunction(TextStream &s, const AbstractMetaTyp
         code.replace(u"%INTYPE_"_s + QString::number(i), typeName);
     }
     replaceCppToPythonVariables(code, getFullTypeNameWithoutModifiers(containerType), true);
-    processCodeSnip(code);
+    processCodeSnip(code, containerType.typeEntry()->qualifiedCppName());
     writeCppToPythonFunction(s, code, fixedCppTypeName(containerType),
                              containerNativeToTargetTypeName(cte));
 }
@@ -3179,8 +3193,9 @@ void CppGenerator::writePythonToCppFunction(TextStream &s, const QString &code,
                                             const QString &targetTypeName) const
 {
     QString prettyCode = code;
-    processCodeSnip(prettyCode);
-    s << "static void " << pythonToCppFunctionName(sourceTypeName, targetTypeName)
+    const QString funcName = pythonToCppFunctionName(sourceTypeName, targetTypeName);
+    processCodeSnip(prettyCode, funcName);
+    s << "static void " << funcName
         << "(PyObject *pyIn, void *cppOut)\n{\n" << indent << prettyCode
         << ensureEndl << outdent << "}\n";
 }
@@ -3283,7 +3298,7 @@ void CppGenerator::writePythonToCppConversionFunctions(TextStream &s,
                     + cpythonTypeNameExt(toNative.sourceType()) + u')';
     }
     typeCheck.replace(u"%in"_s, u"pyIn"_s);
-    processCodeSnip(typeCheck);
+    processCodeSnip(typeCheck, targetType->qualifiedCppName());
     writeIsPythonConvertibleToCppFunction(s, sourceTypeName, targetTypeName, typeCheck);
 }
 
@@ -3756,7 +3771,8 @@ void CppGenerator::writeMethodCall(TextStream &s, const AbstractMetaFunctionCPtr
                         if (!func->isStatic()) {
                             const bool directInheritance = context.metaClass() == ownerClass;
                             mc << (directInheritance ? "static_cast" : "reinterpret_cast")
-                                << "<::" << wrapperName(ownerClass) << " *>(" << CPP_SELF_VAR << ")->";
+                               << '<' << wrapperName(ownerClass) << " *>("
+                               << CPP_SELF_VAR << ")->";
                         }
 
                         if (!func->isAbstract())
@@ -4025,7 +4041,7 @@ void CppGenerator::writeSpecialCastFunction(TextStream &s, const AbstractMetaCla
             s << "else ";
         s << "if (desiredType == " << cpythonTypeNameExt(baseClass->typeEntry())
             << ")\n" << indent
-            << "return static_cast< ::" << baseClass->qualifiedCppName() << " *>(me);\n"
+            << "return static_cast< " << getFullTypeName(baseClass) << " *>(me);\n"
             << outdent;
         firstClass = false;
     }
@@ -5488,12 +5504,10 @@ void CppGenerator::writeClassRegister(TextStream &s,
     if (usePySideExtensions() && isQObject(metaClass)) {
         s << "Shiboken::ObjectType::setSubTypeInitHook(pyType, &PySide::initQObjectSubType);\n"
             << "PySide::initDynamicMetaObject(pyType, &::"
-            << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof(";
-        if (shouldGenerateCppWrapper(metaClass))
-            s << wrapperName(metaClass);
-        else
-            s << "::" << metaClass->qualifiedCppName();
-        s << "));\n";
+            << metaClass->qualifiedCppName() << "::staticMetaObject, sizeof("
+            << (shouldGenerateCppWrapper(metaClass)
+                ? wrapperName(metaClass) : getFullTypeName(metaClass))
+            << "));\n";
     }
 
     s << outdent << "}\n";
@@ -5636,8 +5650,8 @@ void CppGenerator::writeTypeDiscoveryFunction(TextStream &s,
             if (ancestor->isPolymorphic()) {
                 s << "if (instanceType == Shiboken::SbkType< ::"
                     << ancestor->qualifiedCppName() << " >())\n" << indent
-                    << "return dynamic_cast< ::" << metaClass->qualifiedCppName()
-                    << " *>(reinterpret_cast< ::"<< ancestor->qualifiedCppName()
+                    << "return dynamic_cast< " << getFullTypeName(metaClass)
+                    << " *>(reinterpret_cast< "<< getFullTypeName(ancestor)
                     << " *>(cptr));\n" << outdent;
             } else {
                 qCWarning(lcShiboken).noquote().nospace()
@@ -6006,8 +6020,7 @@ bool CppGenerator::finishGeneration()
     const CodeSnipList snips = moduleEntry->codeSnips();
 
     // module inject-code native/beginning
-    if (!snips.isEmpty())
-        writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
+    writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::NativeCode);
 
     // cleanup staticMetaObject attribute
     if (usePySideExtensions()) {
@@ -6137,8 +6150,8 @@ bool CppGenerator::finishGeneration()
         << indent << "return " << globalModuleVar << ";\n" << outdent;
 
     // module inject-code target/beginning
-    if (!snips.isEmpty())
-        writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode);
+    writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning,
+                         TypeSystem::TargetLangCode);
 
     for (const QString &requiredModule : requiredModules) {
         s << "{\n" << indent
@@ -6257,18 +6270,16 @@ bool CppGenerator::finishGeneration()
         << outdent << "}\n";
 
     // module inject-code target/end
-    if (!snips.isEmpty())
-        writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
+    writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::TargetLangCode);
 
     // module inject-code native/end
-    if (!snips.isEmpty())
-        writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
+    writeModuleCodeSnips(s, snips, TypeSystem::CodeSnipPositionEnd, TypeSystem::NativeCode);
 
     if (usePySideExtensions()) {
         for (const AbstractMetaEnum &metaEnum : std::as_const(globalEnums))
             if (!metaEnum.isAnonymous()) {
                 ConfigurableScope configScope(s, metaEnum.typeEntry());
-                s << "qRegisterMetaType< ::" << metaEnum.typeEntry()->qualifiedCppName()
+                s << "qRegisterMetaType< " << getFullTypeName(metaEnum.typeEntry())
                   << " >(\"" << metaEnum.name() << "\");\n";
             }
 
index 54b37fadb46d2bdb7dfd4b84c9fefde8575f8dc8..95ed0bf7dfe53416349016dbc65d19aae495de7f 100644 (file)
@@ -521,6 +521,10 @@ private:
     static void writePyMethodDefs(TextStream &s, const QString &className,
                                   const QString &methodsDefinitions);
 
+    void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+                              TypeSystem::CodeSnipPosition position,
+                              TypeSystem::Language language) const;
+
     static bool hasBoolCast(const AbstractMetaClassCPtr &metaClass)
     { return boolCast(metaClass).has_value(); }
 
index 61ebfe8f3ed6fec25859e2076d3c497ecb28a6eb..9f6de35605bd05d170d65296ca7b0f55456c557a 100644 (file)
@@ -13,6 +13,7 @@
 #include <abstractmetalang_helpers.h>
 #include <codesnip.h>
 #include <clangparser/compilersupport.h>
+#include <exception.h>
 #include <typedatabase.h>
 #include <reporthandler.h>
 #include <textstream.h>
@@ -20,6 +21,7 @@
 #include "containertypeentry.h"
 #include "enumtypeentry.h"
 #include "flagstypeentry.h"
+#include <messages.h>
 #include "namespacetypeentry.h"
 #include "primitivetypeentry.h"
 #include "typedefentry.h"
@@ -593,10 +595,8 @@ bool HeaderGenerator::finishGeneration()
     StringStream macrosStream(TextStream::Language::Cpp);
 
     const auto snips = TypeDatabase::instance()->defaultTypeSystemType()->codeSnips();
-    if (!snips.isEmpty()) {
-        writeCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
-                       TypeSystem::TargetLangCode);
-    }
+    writeModuleCodeSnips(macrosStream, snips, TypeSystem::CodeSnipPositionDeclaration,
+                         TypeSystem::TargetLangCode);
 
     macrosStream << "// Type indices\nenum : int {\n";
     auto classList = api().classes();
@@ -880,8 +880,10 @@ void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaEnum
 
 void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaClassCPtr &cppClass)
 {
-    s <<  "template<> inline PyTypeObject *SbkType< ::" << cppClass->qualifiedCppName() << " >() "
-      <<  "{ return reinterpret_cast<PyTypeObject *>(" << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
+    s << "template<> inline PyTypeObject *SbkType< "
+      << getFullTypeName(cppClass) << " >() "
+      << "{ return reinterpret_cast<PyTypeObject *>("
+      << cpythonTypeNameExt(cppClass->typeEntry()) << "); }\n";
 }
 
 void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType &metaType)
@@ -889,3 +891,16 @@ void HeaderGenerator::writeSbkTypeFunction(TextStream &s, const AbstractMetaType
     s <<  "template<> inline PyTypeObject *SbkType< ::" << metaType.cppSignature() << " >() "
       <<  "{ return " << cpythonTypeNameExt(metaType) << "; }\n";
 }
+
+void HeaderGenerator::writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+                                           TypeSystem::CodeSnipPosition position,
+                                           TypeSystem::Language language) const
+{
+    if (!codeSnips.isEmpty()) {
+        try {
+            writeCodeSnips(s, codeSnips, position, language);
+        } catch (const std::exception &e) {
+            throw Exception(msgSnippetError("module header of "_L1 + moduleName(), e.what()));
+        }
+    }
+}
index 5b5f5a4a14d0eba2c89dbbe8a863e4a8efac909c..e986ff405907213a1740b3e04b74033cf2546856 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "shibokengenerator.h"
 #include "include.h"
+#include "modifications_typedefs.h"
 
 #include <QtCore/QSet>
 
@@ -56,6 +57,9 @@ private:
     void writeWrapperClass(TextStream &s, const QString &wrapperName, const GeneratorContext &classContext) const;
     void writeInheritedWrapperClassDeclaration(TextStream &s,
                                                const GeneratorContext &classContext) const;
+    void writeModuleCodeSnips(TextStream &s, const CodeSnipList &codeSnips,
+                              TypeSystem::CodeSnipPosition position,
+                              TypeSystem::Language language) const;
 
     AbstractMetaClassCList m_alternateTemplateIndexes;
 };
index d4a9e0031021c0930cfd43f97e42fa65e1600741..3830540cfce3ee2bccc636f3aed60bf010a6f8b3 100644 (file)
@@ -561,7 +561,7 @@ QString ShibokenGenerator::cpythonWrapperCPtr(const TypeEntryCPtr &type,
 {
     if (!type->isWrapperType())
         return QString();
-    return u"reinterpret_cast< ::"_s + type->qualifiedCppName()
+    return u"reinterpret_cast< "_s + getFullTypeName(type)
         + u" *>(Shiboken::Conversions::cppPointer("_s + cpythonTypeNameExt(type)
         + u", reinterpret_cast<SbkObject *>("_s + argName + u")))"_s;
 }
@@ -1326,7 +1326,7 @@ void ShibokenGenerator::processClassCodeSnip(QString &code, const GeneratorConte
     code.replace(u"%TYPE"_s, className);
     code.replace(u"%CPPTYPE"_s, metaClass->name());
 
-    processCodeSnip(code);
+    processCodeSnip(code, context.effectiveClassName());
 }
 
 void ShibokenGenerator::processCodeSnip(QString &code) const
@@ -1344,6 +1344,15 @@ void ShibokenGenerator::processCodeSnip(QString &code) const
     replaceTypeCheckTypeSystemVariable(code);
 }
 
+void ShibokenGenerator::processCodeSnip(QString &code, const QString &context) const
+{
+    try {
+        processCodeSnip(code);
+    } catch (const std::exception &e) {
+        throw Exception(msgSnippetError(context, e.what()));
+    }
+}
+
 ShibokenGenerator::ArgumentVarReplacementList
     ShibokenGenerator::getArgumentReplacement(const AbstractMetaFunctionCPtr &func,
                                               bool usePyArgs, TypeSystem::Language language,
@@ -1648,7 +1657,7 @@ void ShibokenGenerator::writeCodeSnips(TextStream &s,
 
     replaceTemplateVariables(code, func);
 
-    processCodeSnip(code);
+    processCodeSnip(code, func->classQualifiedSignature());
     s << "// Begin code injection\n" << code << "// End of code injection\n\n";
 }
 
index 80e94b84b1114198f2622314ec987b651d03347e..25eb7854169e4cf41185ba541970d8ce9162aceb 100644 (file)
@@ -27,7 +27,7 @@ struct GeneratorClassInfoCacheEntry;
 struct IncludeGroup;
 struct ShibokenGeneratorOptions;
 
-QT_FORWARD_DECLARE_CLASS(TextStream)
+class TextStream;
 
 // Function to be used for implementing nb_bool
 struct BoolCastFunction
@@ -145,6 +145,7 @@ protected:
 
     /// Replaces variables for the user's custom code at global or class level.
     void processCodeSnip(QString &code) const;
+    void processCodeSnip(QString &code, const QString &context) const;
     void processClassCodeSnip(QString &code, const GeneratorContext &context) const;
 
     /**
index 05c779fd142974e53c4c6de3c15d40c3ab82a2f6..4638ca6f96a74f8f9f0f0f160e35c71546532698 100644 (file)
@@ -30,7 +30,7 @@ if(SHIBOKEN_IS_CROSS_BUILD)
     set(host_python_path "${QFP_PYTHON_HOST_PATH}")
     set(use_pyc_in_embedding FALSE)
 else()
-    set(host_python_path "${PYTHON_EXECUTABLE}")
+    set(host_python_path "${Python_EXECUTABLE}")
     if(PYTHON_LIMITED_API)
         set(use_pyc_in_embedding FALSE)
     else()
index 828c54a89449ad1fba479ba9970b36d0f58a6299..70e1e2f45110bb94d1ebf8e0a883f5e39f13c253 100644 (file)
@@ -412,7 +412,7 @@ void SbkObjectType_tp_dealloc(PyTypeObject *sbkType)
     auto pyObj = reinterpret_cast<PyObject *>(sbkType);
 
     PyObject_GC_UnTrack(pyObj);
-#ifndef Py_LIMITED_API
+#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
 #  if PY_VERSION_HEX >= 0x030A0000
     Py_TRASHCAN_BEGIN(pyObj, 1);
 #  else
@@ -430,7 +430,7 @@ void SbkObjectType_tp_dealloc(PyTypeObject *sbkType)
             Shiboken::Conversions::deleteConverter(sotp->converter);
         PepType_SOTP_delete(sbkType);
     }
-#ifndef Py_LIMITED_API
+#if !defined(Py_LIMITED_API) && !defined(PYPY_VERSION)
 #  if PY_VERSION_HEX >= 0x030A0000
     Py_TRASHCAN_END;
 #  else
index d2203fb438f4f07f09d527e92f9f2812f3abb42f..9c75c00db03d48f56d99d1731ae08754e2a965b0 100644 (file)
@@ -584,7 +584,7 @@ const char *typeNameOf(const char *typeIdName)
     return result;
 }
 
-#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030A0000
+#if !defined(Py_LIMITED_API) && PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION)
 static int _getPyVerbose()
 {
     PyConfig config;
@@ -597,7 +597,7 @@ int pyVerbose()
 {
 #ifdef Py_LIMITED_API
     return Pep_GetVerboseFlag();
-#elif PY_VERSION_HEX >= 0x030A0000
+#elif PY_VERSION_HEX >= 0x030A0000 && !defined(PYPY_VERSION)
     static const int result = _getPyVerbose();
     return result;
 #else
index 0d29815083a57eb97d2ab07ff83236ef853f10b1..37e51921e6888ca0f9c9798d5a58d56b4df334bc 100644 (file)
@@ -512,6 +512,16 @@ int PepCode_Check(PyObject *o)
 
 #endif // Py_LIMITED_API
 
+#if defined(Py_LIMITED_API) || defined(PYPY_VERSION)
+PyObject *PepFunction_GetDefaults(PyObject *function)
+{
+    auto *ob_ret = PyObject_GetAttrString(function, "__defaults__");
+    Py_XDECREF(ob_ret); // returns borrowed ref
+    return ob_ret != Py_None ? ob_ret : nullptr;
+}
+
+#endif // defined(Py_LIMITED_API) || defined(PYPY_VERSION)
+
 /*****************************************************************************
  *
  * Support for datetime.h
index 5a3157446c033942a70ce4582adcb42470902a10..39a6f3d13a9f6804ec3fb0791cbd243a7a21434a 100644 (file)
@@ -410,6 +410,14 @@ LIBSHIBOKEN_API int PepCode_Check(PyObject *o);
 #  define PepCode_GET_FLAGS(o)         ((o)->co_flags)
 #  define PepCode_GET_ARGCOUNT(o)      ((o)->co_argcount)
 #  define PepCode_Check PyCode_Check
+
+#  ifdef PYPY_VERSION
+
+LIBSHIBOKEN_API PyObject *PepFunction_GetDefaults(PyObject *function);
+
+#    else
+#    define PepFunction_GetDefaults PyFunction_GetDefaults
+#  endif
 #endif
 
 /*****************************************************************************
index 7f99abc3e21955adf1e428d4334f9bfd4991e30a..5c0b38fdb76768e3df80f25dd6abc9fab6da8095 100644 (file)
@@ -10,8 +10,8 @@
 #define SHIBOKEN_MICRO_VERSION @shiboken_MICRO_VERSION@
 #define SHIBOKEN_RELEASE_LEVEL "final"
 #define SHIBOKEN_SERIAL 0
-#define PYTHON_VERSION_MAJOR @PYTHON_VERSION_MAJOR@
-#define PYTHON_VERSION_MINOR @PYTHON_VERSION_MINOR@
-#define PYTHON_VERSION_PATCH @PYTHON_VERSION_PATCH@
+#define PYTHON_VERSION_MAJOR @Python_VERSION_MAJOR@
+#define PYTHON_VERSION_MINOR @Python_VERSION_MINOR@
+#define PYTHON_VERSION_PATCH @Python_VERSION_PATCH@
 
 #endif
index 4588b5917759743e4b71cd26b0789a4cecdb3e27..1af82e316b4ddbd3e99a1eb62845d4a4cea6e0d5 100644 (file)
@@ -1,6 +1,8 @@
 # Copyright (C) 2022 The Qt Company Ltd.
 # SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 
+# flake8: noqa E:203
+
 """
 mapping.py
 
@@ -20,10 +22,12 @@ from pathlib import Path
 from typing import TypeVar, Generic
 from _imp import is_builtin
 
+
 class ellipsis(object):
     def __repr__(self):
         return "..."
 
+
 ellipsis = ellipsis()
 Point = typing.Tuple[int, int]
 Variant = typing.Any
@@ -38,7 +42,7 @@ _S = TypeVar("_S")
 MultiMap = typing.DefaultDict[str, typing.List[str]]
 
 # ulong_max is only 32 bit on windows.
-ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff
+ulong_max = 2 * sys.maxsize + 1 if len(struct.pack("L", 1)) != 4 else 0xffffffff
 ushort_max = 0xffff
 
 GL_COLOR_BUFFER_BIT = 0x00004000
@@ -74,6 +78,7 @@ class _NotCalled(str):
         text = self if self.endswith(")") else self + "()"
         return eval(text, namespace)
 
+
 USE_PEP563 = False
 # Note: we cannot know if this feature has been imported.
 # Otherwise it would be "sys.version_info[:2] >= (3, 7)".
@@ -86,6 +91,7 @@ USE_PEP563 = False
 class Virtual(_NotCalled):
     pass
 
+
 # Other types I simply could not find.
 class Missing(_NotCalled):
     # The string must be quoted, because the object does not exist.
@@ -98,6 +104,7 @@ class Missing(_NotCalled):
 class Invalid(_NotCalled):
     pass
 
+
 # Helper types
 class Default(_NotCalled):
     pass
@@ -106,6 +113,7 @@ class Default(_NotCalled):
 class Instance(_NotCalled):
     pass
 
+
 # Parameterized primitive variables
 class _Parameterized(object):
     def __init__(self, type):
@@ -115,15 +123,18 @@ class _Parameterized(object):
     def __repr__(self):
         return f"{type(self).__name__}({self.type.__name__})"
 
+
 # Mark the primitive variables to be moved into the result.
 class ResultVariable(_Parameterized):
     pass
 
+
 # Mark the primitive variables to become Sequence, Iterable or List
 # (decided in the parser).
 class ArrayLikeVariable(_Parameterized):
     pass
 
+
 StringList = ArrayLikeVariable(str)
 
 
@@ -180,6 +191,7 @@ def check_module(mod):
         mod_name = mod.__name__
         raise ImportError(f"Module '{mod_name}' is not a binary module!")
 
+
 update_mapping = Reloader().update
 type_map = {}
 namespace = globals()  # our module's __dict__
@@ -200,9 +212,9 @@ type_map.update({
     "PyCallable": typing.Callable,
     "PyObject": object,
     "PyObject*": object,
-    "PyArrayObject": ArrayLikeVariable, # numpy
+    "PyArrayObject": ArrayLikeVariable,  # numpy
     "PyPathLike": typing.Union[str, bytes, os.PathLike],
-    "PySequence": typing.Iterable,  # important for numpy
+    "PySequence": typing.Iterable,   # important for numpy
     "PyTypeObject": type,
     "QChar": str,
     "QHash": typing.Dict,
@@ -266,16 +278,16 @@ type_map.update({
     "ulong": int,
     "ULONG_MAX": ulong_max,
     "UINT64_MAX": 0xffffffff,
-    "unsigned char": int, # 5.9
+    "unsigned char": int,  # 5.9
     "unsigned char*": str,
     "unsigned int": int,
-    "unsigned long int": int, # 5.6, RHEL 6.6
+    "unsigned long int": int,  # 5.6, RHEL 6.6
     "unsigned long long": int,
     "unsigned long": int,
-    "unsigned short int": int, # 5.6, RHEL 6.6
+    "unsigned short int": int,  # 5.6, RHEL 6.6
     "unsigned short": int,
     "ushort": int,
-    "void": int, # be more specific?
+    "void": int,  # be more specific?
     "WId": WId,
     "zero(bytes)": b"",
     "zero(Char)": 0,
@@ -289,7 +301,7 @@ type_map.update({
     "numpy.ndarray": typing.List[typing.Any],
     "std.array[int, 4]": typing.List[int],
     "std.array[float, 4]": typing.List[float]
-    })
+})
 
 type_map.update({
     # Handling variables declared as array:
@@ -301,8 +313,8 @@ type_map.update({
     "array long long*"      : ArrayLikeVariable(int),
     "array long*"           : ArrayLikeVariable(int),
     "array short*"          : ArrayLikeVariable(int),
-    "array signed char*"    : bytes,
-    "array unsigned char*"  : bytes,
+    "array signed char*"    : typing.Union[bytes, bytearray, memoryview],
+    "array unsigned char*"  : typing.Union[bytes, bytearray, memoryview],
     "array unsigned int*"   : ArrayLikeVariable(int),
     "array unsigned short*" : ArrayLikeVariable(int),
     # PYSIDE-1646: New macOS primitive types
@@ -313,17 +325,17 @@ type_map.update({
     "array int32_t*"        : ArrayLikeVariable(int),
     "array uint32_t*"       : ArrayLikeVariable(int),
     "array intptr_t*"       : ArrayLikeVariable(int),
-    })
+})
 
 type_map.update({
     # Special cases:
-    "char*"         : bytes,
-    "QChar*"        : bytes,
+    "char*"         : typing.Union[bytes, bytearray, memoryview],
+    "QChar*"        : typing.Union[bytes, bytearray, memoryview],
     "quint32*"      : int,        # only for QRandomGenerator
     "quint8*"       : bytearray,  # only for QCborStreamReader and QCborValue
-    "uchar*"        : bytes,
-    "unsigned char*": bytes,
-    })
+    "uchar*"        : typing.Union[bytes, bytearray, memoryview],
+    "unsigned char*": typing.Union[bytes, bytearray, memoryview],
+})
 
 type_map.update({
     # Handling variables that are returned, eventually as Tuples:
@@ -345,7 +357,7 @@ type_map.update({
     "uint*"         : ResultVariable(int),
     "unsigned int*" : ResultVariable(int),
     "QStringList*"  : ResultVariable(StringList),
-    })
+})
 
 
 type_map.update({
@@ -353,20 +365,21 @@ type_map.update({
     "[typing.Any]"  : [typing.Any],
     "[typing.Any,typing.Any]"  : [typing.Any, typing.Any],
     "None" : None,
-    })
+})
 
 
 # PYSIDE-1328: We need to handle "self" explicitly.
 type_map.update({
     "self" : "self",
     "cls"  : "cls",
-    })
+})
 
 # PYSIDE-1538: We need to treat "std::optional" accordingly.
 type_map.update({
     "std.optional": typing.Optional,
     })
 
+
 # The Shiboken Part
 def init_Shiboken():
     type_map.update({
@@ -376,6 +389,7 @@ def init_Shiboken():
     })
     return locals()
 
+
 def init_minimal():
     type_map.update({
         "MinBool": bool,
@@ -391,7 +405,7 @@ def init_sample():
         "const char*": str,
         "Complex": complex,
         "double": float,
-        "ByteArray&": bytes,
+        "ByteArray&": typing.Union[bytes, bytearray, memoryview],
         "Foo.HANDLE": int,
         "HANDLE": int,
         "Null": None,
@@ -399,7 +413,7 @@ def init_sample():
         "OddBool": bool,
         "PStr": str,
         "PyDate": datetime.date,
-        "PyBuffer": bytes,
+        "PyBuffer": typing.Union[bytes, bytearray, memoryview],
         "sample.bool": bool,
         "sample.char": int,
         "sample.double": float,
@@ -435,6 +449,7 @@ def init_smart():
     # This missing type should be defined in module smart. We cannot set it to Missing()
     # because it is a container type. Therefore, we supply a surrogate:
     global SharedPtr
+
     class SharedPtr(Generic[_S]):
         __module__ = "smart"
     smart.SharedPtr = SharedPtr
@@ -448,7 +463,7 @@ def init_smart():
 def init_PySide6_QtCore():
     from PySide6.QtCore import Qt, QUrl, QDir, QKeyCombination
     from PySide6.QtCore import QRect, QRectF, QSize, QPoint, QLocale, QByteArray
-    from PySide6.QtCore import QMarginsF # 5.9
+    from PySide6.QtCore import QMarginsF  # 5.9
     from PySide6.QtCore import SignalInstance
     try:
         # seems to be not generated by 5.9 ATM.
@@ -459,23 +474,23 @@ def init_PySide6_QtCore():
         "' '": " ",
         "'%'": "%",
         "'g'": "g",
-        "4294967295UL": 4294967295, # 5.6, RHEL 6.6
+        "4294967295UL": 4294967295,  # 5.6, RHEL 6.6
         "CheckIndexOption.NoOption": Instance(
-            "PySide6.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"), # 5.11
+            "PySide6.QtCore.QAbstractItemModel.CheckIndexOptions.NoOption"),  # 5.11
         "DescriptorType(-1)": int,  # Native handle of QSocketDescriptor
         "false": False,
         "list of QAbstractAnimation": typing.List[PySide6.QtCore.QAbstractAnimation],
         "long long": int,
         "size_t": int,
-        "NULL": None, # 5.6, MSVC
-        "nullptr": None, # 5.9
-        "PyBuffer": bytes,
+        "NULL": None,  # 5.6, MSVC
+        "nullptr": None,  # 5.9
+        "PyBuffer": typing.Union[bytes, bytearray, memoryview],
         "PyByteArray": bytearray,
-        "PyBytes": bytes,
+        "PyBytes": typing.Union[bytes, bytearray, memoryview],
         "PyTuple": typing.Tuple,
         "QDeadlineTimer(QDeadlineTimer.Forever)": Instance("PySide6.QtCore.QDeadlineTimer"),
         "PySide6.QtCore.QUrl.ComponentFormattingOptions":
-            PySide6.QtCore.QUrl.ComponentFormattingOption, # mismatch option/enum, why???
+            PySide6.QtCore.QUrl.ComponentFormattingOption,  # mismatch option/enum, why???
         "PyUnicode": typing.Text,
         "QByteArrayView": QByteArray,
         "Q_NULLPTR": None,
@@ -485,15 +500,15 @@ def init_PySide6_QtCore():
             "QDir.Filters(QDir.AllEntries | QDir.NoDotAndDotDot)"),
         "QDir.SortFlags(Name | IgnoreCase)": Instance(
             "QDir.SortFlags(QDir.Name | QDir.IgnoreCase)"),
-        "QEvent.Type.None" : None,
-        "QGenericArgument((0))": ellipsis, # 5.6, RHEL 6.6. Is that ok?
+        "QEvent.Type.None": None,
+        "QGenericArgument((0))": ellipsis,  # 5.6, RHEL 6.6. Is that ok?
         "QGenericArgument()": ellipsis,
         "QGenericArgument(0)": ellipsis,
-        "QGenericArgument(NULL)": ellipsis, # 5.6, MSVC
-        "QGenericArgument(nullptr)": ellipsis, # 5.10
+        "QGenericArgument(NULL)": ellipsis,  # 5.6, MSVC
+        "QGenericArgument(nullptr)": ellipsis,  # 5.10
         "QGenericArgument(Q_NULLPTR)": ellipsis,
         "QJsonObject": typing.Dict[str, PySide6.QtCore.QJsonValue],
-        "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"), # repr is btw. very wrong, fix it?!
+        "QModelIndex()": Invalid("PySide6.QtCore.QModelIndex"),  # repr is btw. very wrong, fix it?!
         "QModelIndexList": typing.List[PySide6.QtCore.QModelIndex],
         "PySideSignalInstance": SignalInstance,
         "QString()": "",
@@ -501,17 +516,16 @@ def init_PySide6_QtCore():
         "QStringList()": [],
         "QStringRef": str,
         "QStringRef": str,
-        "Qt.HANDLE": int, # be more explicit with some constants?
+        "Qt.HANDLE": int,  # be more explicit with some constants?
         "QUrl.FormattingOptions(PrettyDecoded)": Instance(
             "QUrl.FormattingOptions(QUrl.PrettyDecoded)"),
         "QVariant()": Invalid(Variant),
-        "QVariant.Type": type, # not so sure here...
-        "QVariantMap": typing.Dict[str, Variant],
+        "QVariant.Type": type,  # not so sure here...
         "QVariantMap": typing.Dict[str, Variant],
     })
     try:
         type_map.update({
-            "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection, # wrong!
+            "PySide6.QtCore.QMetaObject.Connection": PySide6.QtCore.Connection,  # wrong!
         })
     except AttributeError:
         # this does not exist on 5.9 ATM.
@@ -536,7 +550,7 @@ def init_PySide6_QtConcurrent():
 
 
 def init_PySide6_QtGui():
-    from PySide6.QtGui import QPageLayout, QPageSize # 5.12 macOS
+    from PySide6.QtGui import QPageLayout, QPageSize  # 5.12 macOS
     type_map.update({
         "0.0f": 0.0,
         "1.0f": 1.0,
@@ -546,9 +560,9 @@ def init_PySide6_QtGui():
         "HBITMAP": int,
         "HICON": int,
         "HRGN": int,
-        "QPixmap()": Default("PySide6.QtGui.QPixmap"), # can't create without qApp
-        "QPlatformSurface*": int, # a handle
-        "QVector< QTextLayout.FormatRange >()": [], # do we need more structure?
+        "QPixmap()": Default("PySide6.QtGui.QPixmap"),  # can't create without qApp
+        "QPlatformSurface*": int,  # a handle
+        "QVector< QTextLayout.FormatRange >()": [],  # do we need more structure?
         "uint32_t": int,
         "uint8_t": int,
         "USHRT_MAX": ushort_max,
@@ -562,8 +576,9 @@ def init_PySide6_QtGui():
 
 
 def init_PySide6_QtWidgets():
-    from PySide6.QtWidgets import QWidget, QMessageBox, QStyleOption, QStyleHintReturn, QStyleOptionComplex
-    from PySide6.QtWidgets import QGraphicsItem, QStyleOptionGraphicsItem # 5.9
+    from PySide6.QtWidgets import (QWidget, QMessageBox, QStyleOption,
+                                   QStyleHintReturn, QStyleOptionComplex,
+                                   QGraphicsItem, QStyleOptionGraphicsItem)
     type_map.update({
         "QMessageBox.StandardButtons(Yes | No)": Instance(
             "QMessageBox.StandardButtons(QMessageBox.Yes | QMessageBox.No)"),
@@ -584,7 +599,7 @@ def init_PySide6_QtSql():
     from PySide6.QtSql import QSqlDatabase
     type_map.update({
         "QLatin1StringView(QSqlDatabase.defaultConnection)": QSqlDatabase.defaultConnection,
-        "QVariant.Invalid": Invalid("Variant"), # not sure what I should create, here...
+        "QVariant.Invalid": Invalid("Variant"),  # not sure what I should create, here...
     })
     return locals()
 
@@ -608,7 +623,7 @@ def init_PySide6_QtOpenGL():
     type_map.update({
         "GLbitfield": int,
         "GLenum": int,
-        "GLfloat": float, # 5.6, MSVC 15
+        "GLfloat": float,  # 5.6, MSVC 15
         "GLint": int,
         "GLuint": int,
     })
index f3b0965889ef230d13fe28876a67a14dc8c8a6ec..05f6e9e604ba3af9483c424802579f767d31a060 100644 (file)
@@ -46,13 +46,18 @@ list(SORT TEST_FILES)
 set(test_blacklist "")
 
 if(SHIBOKEN_IS_CROSS_BUILD)
-    # PYTHON_EXECUTABLE will be empty when cross-building.
+    # Python_EXECUTABLE will be empty when cross-building.
     message(WARNING
         "Running tests when cross-compiling is not supported because it would require running "
         "a target python interpreter which might have a different architecture than the host."
     )
 else()
-    find_package(PythonInterp REQUIRED)
+    find_package(
+        Python
+        ${USE_PYTHON_VERSION}
+        REQUIRED
+        COMPONENTS Interpreter Development
+    )
 endif()
 
 if(NOT CTEST_TESTING_TIMEOUT)
@@ -66,7 +71,7 @@ foreach(test_file ${TEST_FILES})
     string(REGEX MATCH "/([^/]+)(binding|module)/([^/]+)_test.py" tmp ${test_file})
     set(test_name "${CMAKE_MATCH_1}_${CMAKE_MATCH_3}")
     list(FIND test_blacklist ${test_name} expect_fail)
-    add_test(${test_name} ${PYTHON_EXECUTABLE} ${test_file})
+    add_test(${test_name} ${Python_EXECUTABLE} ${test_file})
     set_tests_properties(${test_name} PROPERTIES ENVIRONMENT "BUILD_DIR=${BUILD_DIR}")
     set_tests_properties(${test_name} PROPERTIES TIMEOUT ${CTEST_TESTING_TIMEOUT})
     if (${expect_fail} GREATER -1)
index 9b9a5ad902243bce9181238f9595541cdb53d269..a1f955752aa84b205d50e76d0dcdaf85e26b249f 100644 (file)
@@ -7,17 +7,19 @@ import unittest
 
 from pathlib import Path
 sys.path.append(os.fspath(Path(__file__).resolve().parents[1]))
-from shiboken_paths import init_paths
+from shiboken_paths import init_paths  # noqa: E402
 init_paths()
 
-from shiboken6 import Shiboken
-from sample import *
+from shiboken6 import Shiboken  # noqa: E402
+from sample import BlackBox, ObjectType, ObjectModel, ObjectView, Point  # noqa: E402
+
 
 class MultipleInherited (ObjectType, Point):
     def __init__(self):
         ObjectType.__init__(self)
         Point.__init__(self)
 
+
 class TestShiboken(unittest.TestCase):
     def testIsValid(self):
         self.assertTrue(Shiboken.isValid(object()))
@@ -55,7 +57,7 @@ class TestShiboken(unittest.TestCase):
         p = ObjectType()
         obj = ObjectType(p)
         obj2 = ObjectType(obj)
-        obj3 = ObjectType(obj)
+        obj3 = ObjectType(obj)  # noqa: F841
         self.assertEqual(Shiboken.dump(None), "Ordinary Python type.")
         Shiboken.dump(obj)
 
@@ -69,9 +71,9 @@ class TestShiboken(unittest.TestCase):
 
         # Don't crash even after deleting an object
         Shiboken.invalidate(obj)
-        Shiboken.dump(obj)  # deleted
-        Shiboken.dump(p)    # child deleted
-        Shiboken.dump(obj2) # parent deleted
+        Shiboken.dump(obj)   # deleted
+        Shiboken.dump(p)     # child deleted
+        Shiboken.dump(obj2)  # parent deleted
 
     def testDelete(self):
         obj = ObjectType()
index d1ec7b1402f59c74ad13c3d5981b399db812c029..f82191f91a37d4612fa66f86d831b5a5faa59a9b 100644 (file)
@@ -77,6 +77,7 @@ class BuildLog(object):
         # we take the latest build for now.
         build_history.sort()
         self.history = build_history
+        self.python_version = None
         self._buildno = None
         if not is_ci:
             # there seems to be a timing problem in RHel 7.6, so we better don't touch it
@@ -137,6 +138,9 @@ class BuildLog(object):
             path = self.selected.build_dir
             base = os.path.basename(path)
             res.extend(base.split("-"))
+        # add exact Python version
+        if self.python_version:
+            res.append("py" + ".".join(map(str, self.python_version)))
         # add all the python and qt subkeys
         for entry in res:
             parts = entry.split(".")
@@ -149,5 +153,8 @@ class BuildLog(object):
         res.append(platform.processor())
         return res
 
+    def set_python_version(self, version_triple):
+        self.python_version = version_triple
+
 
 builds = BuildLog()
index c73f269fdae20b6eb83c08143c5b5941324b8301..31a48f87c957a63878e4dfa19047487525e7b30e 100644 (file)
@@ -79,6 +79,8 @@ def test_project(project, args, blacklist, runs):
     for idx in range(runs):
         index = idx + 1
         runner = TestRunner(builds.selected, project, index)
+        # For the full Python version we need to ask the TestRunner.
+        builds.set_python_version(runner.get_python_version())
         print()
         print(f"********* Start testing of {project} *********")
         print("Config: Using", " ".join(builds.classifiers))
index 0d3b4c8926b40f1311af7e9424941dc5882e8fb6..b52ac49370a30709c78576ac1e3832f59c43ff5d 100644 (file)
@@ -36,6 +36,31 @@ class TestRunner(object):
         self._setup_clang()
         self._setup()
 
+    def get_python_version(self):
+        """
+        Finding the exact Python version.
+        ---------------------------------
+
+        This is done by asking the interpreter, because it cannot reliably
+        be found from any file name parsing as a triple.
+
+        Note: We need to look into the CMakeCache.txt file to find out
+        what CMake has found as the Python interpreter to use.
+        This is *not* necessarily the same Python that runs this script,
+        otherwise we could use the version info directly.
+        """
+        look_python = os.path.join(self.test_dir, "CMakeCache.txt")
+        look_for = "PYTHON_EXECUTABLE:FILEPATH="
+        with open(look_python) as f:
+            for line in f:
+                if line.startswith(look_for):
+                    python_exec = line.split("=")[-1].strip()
+                    res = subprocess.run([python_exec, "-c",
+                                         "import sys;print(sys.version_info[:3])"],
+                                         capture_output=True)
+                    return eval(res.stdout.decode("utf-8"))
+        return None
+
     def _setup_clang(self):
         if sys.platform != "win32":
             return
index 70c43313d99e94fb30e8705ca3522eddf25ac730..24bc890bc0fddd4e0c247614e7268789063118fd 100644 (file)
@@ -35,9 +35,9 @@ this_dir = os.path.dirname(this_file)
 setup_script_dir = os.path.abspath(os.path.join(this_dir, ".."))
 sys.path.append(setup_script_dir)
 
-from build_scripts.utils import (find_files_using_glob, find_glob_in_path,
+from build_scripts.utils import (find_files_using_glob, find_glob_in_path,  # noqa: E402
                                  remove_tree, run_process, run_process_output)
-from build_scripts.log import log
+from build_scripts.log import log  # noqa: E402
 
 log.setLevel(logging.DEBUG)
 
@@ -83,7 +83,8 @@ def package_prefix_names():
     # Note: shiboken6_generator is not needed for compile_using_nuitka,
     # but building modules with cmake needs it.
     if NEW_WHEELS:
-        return ["shiboken6", "shiboken6_generator", "PySide6_Essentials", "PySide6_Addons", "PySide6"]
+        return ["shiboken6", "shiboken6_generator", "PySide6_Essentials", "PySide6_Addons",
+                "PySide6"]
     else:
         return ["shiboken6", "shiboken6_generator", "PySide6"]
 
@@ -360,7 +361,7 @@ def try_build_examples():
             for modname in modules:
                 # PYSIDE-1735: pyi files are no longer compatible with Python.
                 # XXX Maybe add a test with Mypy here?
-                pass # execute_script(src_path / f"{modname}.pyi")
+                pass  # execute_script(src_path / f"{modname}.pyi")
 
 
 def run_wheel_tests(install_wheels, wheels_dir_name):
@@ -392,7 +393,8 @@ if __name__ == "__main__":
     )
     parser.add_argument("--qmake", type=str, help="Path to qmake")
     parser.add_argument("--cmake", type=str, help="Path to cmake")
-    parser.add_argument("--wheels-dir", type=str, help="Path to where the wheels are", default="dist")
+    parser.add_argument("--wheels-dir", type=str, help="Path to where the wheels are",
+                        default="dist")
     parser.add_argument("--new", action="store_true", help="Option to test new wheels")
     options = parser.parse_args()
     QMAKE_PATH = find_executable("qmake", options.qmake)
index e9363117355724cb06bea426210ee0acc8fb7f16..b09ab7bd9d3a6c389f543d1b8a5556bf8873cdcc 100644 (file)
@@ -126,14 +126,14 @@ def download_android_ndk(ndk_path: Path):
         print(f"NDK path found in {str(ndk_version_path)}")
     else:
         ndk_path.mkdir(parents=True, exist_ok=True)
-        url = f"https://dl.google.com/android/repository/android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"
+        url = (f"https://dl.google.com/android/repository"
+               f"/android-ndk-r{ANDROID_NDK_VERSION}-linux.zip")
 
         print(f"Downloading Android Ndk version r{ANDROID_NDK_VERSION}")
         _download(url=url, destination=ndk_zip_path)
 
         print("Unpacking Android Ndk")
-        _unpack(zip_file=(ndk_path /
-                f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"),
+        _unpack(zip_file=(ndk_path / f"android-ndk-r{ANDROID_NDK_VERSION}-linux.zip"),
                 destination=ndk_path)
 
     return ndk_version_path
index 97f4030acaa144e470699d61b1a3c93dc47c7798..9c4616e97826818787419c105eacd2e8bab9ec8f 100644 (file)
@@ -4,12 +4,10 @@
 import sys
 import logging
 import argparse
-import tempfile
-import subprocess
 import stat
 import warnings
+import shutil
 from dataclasses import dataclass
-from typing import List
 
 from pathlib import Path
 from git import Repo, RemoteProgress
@@ -17,22 +15,11 @@ from tqdm import tqdm
 from jinja2 import Environment, FileSystemLoader
 
 from android_utilities import (run_command, download_android_commandlinetools,
-                                download_android_ndk, install_android_packages)
+                               download_android_ndk, install_android_packages)
 
 # Note: Does not work with PyEnv. Your Host Python should contain openssl.
 PYTHON_VERSION = "3.10"
 
-APIC_HELP = ('''
-Points to the installation path of Python for the specific Android
-platform. If the path given does not exist, then Python for Android
-is cross compiled for the specific platform and installed into this
-path as <path>/Python-'plat_name'/_install.
-
-If this path is not given, then Python for Android is cross-compiled
-into a temportary directory, which is deleted when the Qt for Python
-Android wheels are created.
-''')
-
 SKIP_UPDATE_HELP = ("skip the updation of SDK packages build-tools, platform-tools to"
                     " latest version")
 
@@ -41,6 +28,21 @@ Accepts license automatically for Android SDK installation. Otherwise,
 accept the license manually through command line.
 ''')
 
+CLEAN_CACHE_HELP = ('''
+Cleans cache stored in $HOME/.pyside6_deploy_cache.
+Options:
+
+1. all - all the cache including Android Ndk, Android Sdk and Cross-compiled Python are deleted.
+2. ndk - Only the Android Ndk is deleted.
+3. sdk - Only the Android Sdk is deleted.
+4. python - The cross compiled Python for all platforms, the cloned CPython, the cross compilation
+            scripts for all platforms are deleted.
+5. toolchain - The CMake toolchain file required for cross-compiling Qt for Python, for all
+               platforms are deleted.
+
+If --clean-cache is used and no explicit value is suppied, then `all` is used as default.
+''')
+
 
 @dataclass
 class PlatformData:
@@ -77,14 +79,15 @@ if __name__ == "__main__":
         formatter_class=argparse.RawTextHelpFormatter,
     )
 
-    parser.add_argument("-p", "--plat-name", type=str, required=True,
+    parser.add_argument("-p", "--plat-name", type=str, nargs="*",
                         choices=["aarch64", "armv7a", "i686", "x86_64"],
-                        help="Android target platform name")
+                        default=["aarch64", "armv7a", "i686", "x86_64"], dest="plat_names",
+                        help="Android target platforms")
 
     parser.add_argument("-v", "--verbose", help="run in verbose mode", action="store_const",
                         dest="loglevel", const=logging.INFO)
     parser.add_argument("--api-level", type=str, default="31", help="Android API level to use")
-    parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred 25b)")
+    parser.add_argument("--ndk-path", type=str, help="Path to Android NDK (Preferred r25c)")
     # sdk path is needed to compile all the Qt Java Acitivity files into Qt6AndroidBindings.jar
     parser.add_argument("--sdk-path", type=str, help="Path to Android SDK")
     parser.add_argument("--qt-install-path", type=str, required=not occp_exists(),
@@ -93,10 +96,6 @@ if __name__ == "__main__":
     parser.add_argument("-occp", "--only-cross-compile-python", action="store_true",
                         help="Only cross compiles Python for the specified Android platform")
 
-    parser.add_argument("-apic", "--android-python-install-path", type=str, default=None,
-                        required=occp_exists(),
-                        help=APIC_HELP)
-
     parser.add_argument("--dry-run", action="store_true", help="show the commands to be run")
 
     parser.add_argument("--skip-update", action="store_true",
@@ -105,6 +104,10 @@ if __name__ == "__main__":
     parser.add_argument("--auto-accept-license", action="store_true",
                         help=ACCEPT_LICENSE_HELP)
 
+    parser.add_argument("--clean-cache", type=str, nargs="?", const="all",
+                        choices=["all", "python", "ndk", "sdk", "toolchain"],
+                        help=CLEAN_CACHE_HELP)
+
     args = parser.parse_args()
 
     logging.basicConfig(level=args.loglevel)
@@ -113,21 +116,43 @@ if __name__ == "__main__":
     ndk_path = args.ndk_path
     sdk_path = args.sdk_path
     only_py_cross_compile = args.only_cross_compile_python
-    python_path = args.android_python_install_path
-    # the same android platforms are named differently in CMake, Cpython and Qt.
-    # Hence, we need to distinguish them
-    qt_plat_name = None
     android_abi = None
     gcc_march = None
     plat_bits = None
     dry_run = args.dry_run
-    plat_name = args.plat_name
+    plat_names = args.plat_names
     api_level = args.api_level
     skip_update = args.skip_update
     auto_accept_license = args.auto_accept_license
+    clean_cache = args.clean_cache
 
     # auto download Android NDK and SDK
     pyside6_deploy_cache = Path.home() / ".pyside6_android_deploy"
+    logging.info(f"Cache created at {str(pyside6_deploy_cache.resolve())}")
+    pyside6_deploy_cache.mkdir(exist_ok=True)
+
+    if pyside6_deploy_cache.exists() and clean_cache:
+        if clean_cache == "all":
+            shutil.rmtree(pyside6_deploy_cache)
+        elif clean_cache == "ndk":
+            cached_ndk_dir = pyside6_deploy_cache / "android-ndk"
+            if cached_ndk_dir.exists():
+                shutil.rmtree(cached_ndk_dir)
+        elif clean_cache == "sdk":
+            cached_sdk_dir = pyside6_deploy_cache / "android-sdk"
+            if cached_sdk_dir.exists():
+                shutil.rmtree(cached_sdk_dir)
+        elif clean_cache == "python":
+            cached_cpython_dir = pyside6_deploy_cache / "cpython"
+            if cached_cpython_dir.exists():
+                shutil.rmtree(pyside6_deploy_cache / "cpython")
+            for cc_python_path in pyside6_deploy_cache.glob("Python-*"):
+                if cc_python_path.is_dir():
+                    shutil.rmtree(cc_python_path)
+        elif clean_cache == "toolchain":
+            for toolchain_path in pyside6_deploy_cache.glob("toolchain_*"):
+                if toolchain_path.is_file():
+                    toolchain_path.unlink()
 
     if not ndk_path:
         # Download android ndk
@@ -140,80 +165,79 @@ if __name__ == "__main__":
         install_android_packages(android_sdk_dir=sdk_path, android_api=api_level, dry_run=dry_run,
                                  accept_license=auto_accept_license, skip_update=skip_update)
 
-    # python path is valid, if Python for android installation exists in python_path
-    valid_python_path = True
-    if python_path and Path(python_path).exists():
-        expected_dirs = ["lib", "include"]
-        for expected_dir in expected_dirs:
-            if not (Path(python_path) / expected_dir).is_dir():
-                valid_python_path = False
-                warnings.warn(
-                    "Given target Python, given through --android-python-install-path does not"
-                    "contain Python. New Python for android will be cross compiled and installed"
-                    "in this directory"
-                )
-                break
-
     templates_path = Path(__file__).parent / "templates"
 
-    # for armv7a the API level dependent binaries like clang are named
-    # armv7a-linux-androideabi27-clang, as opposed to other platforms which
-    # are named like x86_64-linux-android27-clang
-    platform_data = None
-    if plat_name == "armv7a":
-        platform_data = PlatformData("armv7a", f"eabi{api_level}", "armeabi-v7a", "armv7", "armv7",
-                                     "32")
-    elif plat_name == "aarch64":
-        platform_data = PlatformData("aarch64", api_level, "arm64-v8a", "arm64_v8a", "armv8-a", "64")
-    elif plat_name == "i686":
-        platform_data = PlatformData("i686", api_level, "x86", "x86", "i686", "32")
-    else:  # plat_name is x86_64
-        platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
-
-    # clone cpython and checkout 3.10
-    with tempfile.TemporaryDirectory() as temp_dir:
+    for plat_name in plat_names:
+        # for armv7a the API level dependent binaries like clang are named
+        # armv7a-linux-androideabi27-clang, as opposed to other platforms which
+        # are named like x86_64-linux-android27-clang
+        platform_data = None
+        if plat_name == "armv7a":
+            platform_data = PlatformData("armv7a", api_level, "armeabi-v7a", "armv7",
+                                         "armv7", "32")
+        elif plat_name == "aarch64":
+            platform_data = PlatformData("aarch64", api_level, "arm64-v8a", "arm64_v8a", "armv8-a",
+                                         "64")
+        elif plat_name == "i686":
+            platform_data = PlatformData("i686", api_level, "x86", "x86", "i686", "32")
+        else:  # plat_name is x86_64
+            platform_data = PlatformData("x86_64", api_level, "x86_64", "x86_64", "x86-64", "64")
+
+        # python path is valid, if Python for android installation exists in python_path
+        python_path = (pyside6_deploy_cache / f"Python-{platform_data.plat_name}-linux-android"
+                       / "_install")
+        valid_python_path = python_path.exists()
+        if Path(python_path).exists():
+            expected_dirs = ["lib", "include"]
+            for expected_dir in expected_dirs:
+                if not (Path(python_path) / expected_dir).is_dir():
+                    valid_python_path = False
+                    warnings.warn(
+                        f"{str(python_path.resolve())} is corrupted. New Python for {plat_name} "
+                        f"android will be cross-compiled into {str(pyside6_deploy_cache.resolve())}"
+                    )
+                    break
+
         environment = Environment(loader=FileSystemLoader(templates_path))
-        temp_dir = Path(temp_dir)
-        logging.info(f"temp dir created at {temp_dir}")
-        if not python_path or not valid_python_path:
-            cpython_dir = temp_dir / "cpython"
-            python_ccompile_script = cpython_dir / "cross_compile.sh"
-
-            logging.info(f"cloning cpython {PYTHON_VERSION}")
-            Repo.clone_from(
-                "https://github.com/python/cpython.git",
-                cpython_dir,
-                progress=CloneProgress(),
-                branch=PYTHON_VERSION,
-            )
+        if not valid_python_path:
+            # clone cpython and checkout 3.10
+            cpython_dir = pyside6_deploy_cache / "cpython"
+            python_ccompile_script = cpython_dir / f"cross_compile_{plat_name}.sh"
+
+            if not cpython_dir.exists():
+                logging.info(f"cloning cpython {PYTHON_VERSION}")
+                Repo.clone_from(
+                    "https://github.com/python/cpython.git",
+                    cpython_dir,
+                    progress=CloneProgress(),
+                    branch=PYTHON_VERSION,
+                )
 
-            if not python_path:
-                android_py_install_path_prefix = temp_dir
-            else:
-                android_py_install_path_prefix = python_path
+            if not python_ccompile_script.exists():
+                # use jinja2 to create cross_compile.sh script
+                template = environment.get_template("cross_compile.tmpl.sh")
+                content = template.render(
+                    plat_name=platform_data.plat_name,
+                    ndk_path=ndk_path,
+                    api_level=platform_data.api_level,
+                    android_py_install_path_prefix=pyside6_deploy_cache,
+                )
 
-            # use jinja2 to create cross_compile.sh script
-            template = environment.get_template("cross_compile.tmpl.sh")
-            content = template.render(
-                plat_name=platform_data.plat_name,
-                ndk_path=ndk_path,
-                api_level=platform_data.api_level,
-                android_py_install_path_prefix=android_py_install_path_prefix,
-            )
+                logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
+                with open(python_ccompile_script, mode="w", encoding="utf-8") as ccompile_script:
+                    ccompile_script.write(content)
 
-            logging.info(f"Writing Python cross compile script into {python_ccompile_script}")
-            with open(python_ccompile_script, mode="w", encoding="utf-8") as ccompile_script:
-                ccompile_script.write(content)
+                # give run permission to cross compile script
+                python_ccompile_script.chmod(python_ccompile_script.stat().st_mode | stat.S_IEXEC)
 
-            # give run permission to cross compile script
-            python_ccompile_script.chmod(python_ccompile_script.stat().st_mode | stat.S_IEXEC)
+            # clean built files
+            logging.info("Cleaning CPython built files")
+            run_command(["make", "distclean"], cwd=cpython_dir, dry_run=dry_run, ignore_fail=True)
 
             # run the cross compile script
             logging.info(f"Running Python cross-compile for platform {platform_data.plat_name}")
-            run_command(["./cross_compile.sh"], cwd=cpython_dir, dry_run=dry_run, show_stdout=True)
-
-            python_path = (f"{android_py_install_path_prefix}/Python-{platform_data.plat_name}-linux-android/"
-                           "_install")
+            run_command([f"./{python_ccompile_script.name}"], cwd=cpython_dir, dry_run=dry_run,
+                        show_stdout=True)
 
             # run patchelf to change the SONAME of libpython from libpython3.x.so.1.0 to
             # libpython3.x.so, to match with python_for_android's Python library. Otherwise,
@@ -224,46 +248,52 @@ if __name__ == "__main__":
 
             logging.info(
                 f"Cross compile Python for Android platform {platform_data.plat_name}. "
-                f"Final installation in "
-                f"{python_path}"
+                f"Final installation in {python_path}"
             )
 
             if only_py_cross_compile:
-                sys.exit(0)
-
-        qfp_toolchain = temp_dir / f"toolchain_{platform_data.plat_name}.cmake"
-        template = environment.get_template("toolchain_default.tmpl.cmake")
-        content = template.render(
-            ndk_path=ndk_path,
-            sdk_path=sdk_path,
-            api_level=platform_data.api_level,
-            qt_install_path=qt_install_path,
-            plat_name=platform_data.plat_name,
-            android_abi=platform_data.android_abi,
-            qt_plat_name=platform_data.qt_plat_name,
-            gcc_march=platform_data.gcc_march,
-            plat_bits=platform_data.plat_bits,
-            python_version=PYTHON_VERSION,
-            target_python_path=python_path
-        )
-
-        logging.info(f"Writing Qt for Python toolchain file into"
-                     f"{qfp_toolchain}")
-        with open(qfp_toolchain, mode="w", encoding="utf-8") as ccompile_script:
-            ccompile_script.write(content)
-
-        # give run permission to cross compile script
-        qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
+                continue
+
+        if only_py_cross_compile:
+            requested_platforms = ",".join(plat_names)
+            print(f"Python for Android platforms: {requested_platforms} cross compiled "
+                  f"to {str(pyside6_deploy_cache)}")
+            sys.exit(0)
+
+        qfp_toolchain = pyside6_deploy_cache / f"toolchain_{platform_data.plat_name}.cmake"
+
+        if not qfp_toolchain.exists():
+            template = environment.get_template("toolchain_default.tmpl.cmake")
+            content = template.render(
+                ndk_path=ndk_path,
+                sdk_path=sdk_path,
+                api_level=platform_data.api_level,
+                qt_install_path=qt_install_path,
+                plat_name=platform_data.plat_name,
+                android_abi=platform_data.android_abi,
+                qt_plat_name=platform_data.qt_plat_name,
+                gcc_march=platform_data.gcc_march,
+                plat_bits=platform_data.plat_bits,
+                python_version=PYTHON_VERSION,
+                target_python_path=python_path
+            )
+
+            logging.info(f"Writing Qt for Python toolchain file into {qfp_toolchain}")
+            with open(qfp_toolchain, mode="w", encoding="utf-8") as ccompile_script:
+                ccompile_script.write(content)
+
+            # give run permission to cross compile script
+            qfp_toolchain.chmod(qfp_toolchain.stat().st_mode | stat.S_IEXEC)
 
         # run the cross compile script
         logging.info(f"Running Qt for Python cross-compile for platform {platform_data.plat_name}")
         qfp_ccompile_cmd = [sys.executable, "setup.py", "bdist_wheel", "--parallel=9",
-                            "--ignore-git", "--standalone", "--limited-api=yes",
+                            "--standalone", "--limited-api=yes",
                             f"--cmake-toolchain-file={str(qfp_toolchain.resolve())}",
                             f"--qt-host-path={qt_install_path}/gcc_64",
                             f"--plat-name=android_{platform_data.plat_name}",
                             f"--python-target-path={python_path}",
                             (f"--qt-target-path={qt_install_path}/"
-                             f"android_{platform_data.qt_plat_name}"),
-                            "--no-qt-tools", "--skip-docs", "--unity"]
+                                f"android_{platform_data.qt_plat_name}"),
+                            "--no-qt-tools", "--unity"]
         run_command(qfp_ccompile_cmd, cwd=pyside_setup_dir, dry_run=dry_run, show_stdout=True)
index ccf0cf8eae7fb077985e1329cfadd8fa8e99b552..85d032e2b21e5d21757a33886647ad50ff58e495 100644 (file)
@@ -6,11 +6,17 @@ export HOST_ARCH={{ plat_name }}-linux-android
 export TOOLCHAIN={{ ndk_path }}/toolchains/llvm/prebuilt/linux-x86_64/bin
 export TOOL_PREFIX=$TOOLCHAIN/$HOST_ARCH
 export PLATFORM_API={{ api_level }}
+{% if plat_name == "armv7a" -%}
+export CXX=${TOOL_PREFIX}eabi${PLATFORM_API}-clang++
+export CPP="${TOOL_PREFIX}eabi${PLATFORM_API}-clang++ -E"
+export CC=${TOOL_PREFIX}eabi${PLATFORM_API}-clang
+{% else %}
 export CXX=${TOOL_PREFIX}${PLATFORM_API}-clang++
 export CPP="${TOOL_PREFIX}${PLATFORM_API}-clang++ -E"
+export CC=${TOOL_PREFIX}${PLATFORM_API}-clang
+{% endif %}
 export AR=$TOOLCHAIN/llvm-ar
 export RANLIB=$TOOLCHAIN/llvm-ranlib
-export CC=$TOOL_PREFIX${PLATFORM_API}-clang
 export LD=$TOOLCHAIN/ld
 export READELF=$TOOLCHAIN/llvm-readelf
 export CFLAGS='-fPIC -DANDROID'
index 3433899981ed0b4cd1fe4c968fb05c094f3fd2d4..a691d50f29582d4f6b058626b465c0443d2d5a1f 100644 (file)
@@ -5,7 +5,11 @@
 cmake_minimum_required(VERSION 3.18)
 include_guard(GLOBAL)
 set(CMAKE_SYSTEM_NAME Android)
+{% if plat_name == "armv7a" -%}
+set(CMAKE_SYSTEM_PROCESSOR armv7-a)
+{% else %}
 set(CMAKE_SYSTEM_PROCESSOR {{ plat_name }})
+{% endif %}
 set(CMAKE_ANDROID_API {{ api_level }})
 set(CMAKE_ANDROID_NDK {{ ndk_path }})
 set(CMAKE_ANDROID_ARCH_ABI {{ android_abi }})
@@ -15,8 +19,12 @@ if(NOT DEFINED ANDROID_PLATFORM AND NOT DEFINED ANDROID_NATIVE_API_LEVEL)
     set(ANDROID_PLATFORM "android-{{ api_level }}" CACHE STRING "")
 endif()
 set(ANDROID_SDK_ROOT {{ sdk_path }})
-
-set(QT_COMPILER_FLAGS "--target={{ plat_name }}-linux-android{{ api_level }} \
+{% if plat_name == "armv7a" -%}
+set(_TARGET_NAME_ENDING "eabi{{ api_level }}")
+{% else %}
+set(_TARGET_NAME_ENDING "{{ api_level }}")
+{% endif %}
+set(QT_COMPILER_FLAGS "--target={{ plat_name }}-linux-android${_TARGET_NAME_ENDING} \
                        -fomit-frame-pointer \
                        -march={{ gcc_march }} \
                        -msse4.2 \
index 7d3982c6919b3b03f276ea6932d3f67146e38282..d46f4db02827c25a7d7863cc62da46e0e2218da6 100644 (file)
@@ -54,7 +54,7 @@ def required_typesystems(module):
         parser.setContentHandler(handler)
         parser.parse(typesystem_file)
     except Exception as e:
-        print(f"Error parsing {typesystem_file}: {e}", file=sys.stderr)
+        print(f"Warning: XML error parsing {typesystem_file}: {e}", file=sys.stderr)
     return handler.required_modules
 
 
index 22da9c895390fd821589a6fb3053152e4890f108..9e8546e80f592d7e05f09d9aaaad8de35573c47e 100644 (file)
@@ -13,6 +13,7 @@ dynamic = ["version"]
 requires-python = ">=3.8, <3.13"
 keywords = ["Qt"]
 license = {text = "LGPL"}
+dependencies = PROJECT_DEPENDENCIES
 classifiers = [
     "Development Status :: 5 - Production/Stable",
     "Environment :: Console",
@@ -50,12 +51,11 @@ Repository = "https://code.qt.io/cgit/pyside/pyside-setup.git/"
 Changelog = "https://code.qt.io/cgit/pyside/pyside-setup.git/tree/doc/changelogs"
 Tracker = "https://bugreports.qt.io/projects/PYSIDE"
 
+PROJECT_SCRIPTS
+
 [tool.distutils.bdist_wheel]
 py_limited_api = "cp38"
 plat_name = PROJECT_TAG
 
-[tool.setuptools.packages]
-find = {}
-
 [tool.setuptools.dynamic]
 version = {attr = PROJECT_VERSION}
index e22298b99ae9f123a8e147d7fd4795c541c7ceec..d65f746eeaf984eae05a0215226cc5137ef21c8a 100644 (file)
@@ -20,9 +20,7 @@ class build_ext(Command):
 setup_args = dict(
     include_package_data=True,
     packages = ["{name}"],
-    entry_points = {console_scripts},
     ext_modules = [Extension("{fake_ext}", [], py_limited_api=True)],
-    install_requires={install},
     cmdclass=dict([("build_ext", build_ext)]),
 )
 setup(**setup_args)